broke up big function to describe PrivDataField

This commit is contained in:
Joey Hess 2014-12-14 16:14:05 -04:00
parent 71723ca09f
commit 23399416f1
8 changed files with 802 additions and 781 deletions

File diff suppressed because it is too large Load Diff

View File

@ -48,30 +48,30 @@ import Utility.Table
-- being used, which is necessary to ensure that the privdata is sent to -- being used, which is necessary to ensure that the privdata is sent to
-- the remote host by propellor. -- the remote host by propellor.
withPrivData withPrivData
:: IsContext c :: (IsContext c, IsPrivDataSource s)
=> PrivDataField => s
-> c -> c
-> (((PrivData -> Propellor Result) -> Propellor Result) -> Property) -> (((PrivData -> Propellor Result) -> Propellor Result) -> Property)
-> Property -> Property
withPrivData field = withPrivData' snd [field] withPrivData s = withPrivData' snd [s]
-- Like withPrivData, but here any of a list of PrivDataFields can be used. -- Like withPrivData, but here any of a list of PrivDataFields can be used.
withSomePrivData withSomePrivData
:: IsContext c :: (IsContext c, IsPrivDataSource s)
=> [PrivDataField] => [s]
-> c -> c
-> ((((PrivDataField, PrivData) -> Propellor Result) -> Propellor Result) -> Property) -> ((((PrivDataField, PrivData) -> Propellor Result) -> Propellor Result) -> Property)
-> Property -> Property
withSomePrivData = withPrivData' id withSomePrivData = withPrivData' id
withPrivData' withPrivData'
:: IsContext c :: (IsContext c, IsPrivDataSource s)
=> ((PrivDataField, PrivData) -> v) => ((PrivDataField, PrivData) -> v)
-> [PrivDataField] -> [s]
-> c -> c
-> (((v -> Propellor Result) -> Propellor Result) -> Property) -> (((v -> Propellor Result) -> Propellor Result) -> Property)
-> Property -> Property
withPrivData' feed fieldlist c mkprop = addinfo $ mkprop $ \a -> withPrivData' feed srclist c mkprop = addinfo $ mkprop $ \a ->
maybe missing (a . feed) =<< getM get fieldlist maybe missing (a . feed) =<< getM get fieldlist
where where
get field = do get field = do
@ -82,14 +82,15 @@ withPrivData' feed fieldlist c mkprop = addinfo $ mkprop $ \a ->
Context cname <- mkHostContext hc <$> asks hostName Context cname <- mkHostContext hc <$> asks hostName
warningMessage $ "Missing privdata " ++ intercalate " or " fieldnames ++ " (for " ++ cname ++ ")" warningMessage $ "Missing privdata " ++ intercalate " or " fieldnames ++ " (for " ++ cname ++ ")"
liftIO $ putStrLn $ "Fix this by running:" liftIO $ putStrLn $ "Fix this by running:"
liftIO $ forM_ fieldlist $ \f -> do liftIO $ forM_ srclist $ \src -> do
putStrLn $ " propellor --set '" ++ show f ++ "' '" ++ cname ++ "'" putStrLn $ " propellor --set '" ++ show (privDataField src) ++ "' '" ++ cname ++ "'"
putStrLn $ " < ( " ++ howtoMkPrivDataField f ++ " )" maybe noop (\d -> putStrLn $ " " ++ d) (describePrivDataSource src)
putStrLn "" putStrLn ""
return FailedChange return FailedChange
addinfo p = p { propertyInfo = propertyInfo p <> mempty { _privDataFields = fieldset } } addinfo p = p { propertyInfo = propertyInfo p <> mempty { _privDataFields = fieldset } }
fieldnames = map show fieldlist fieldnames = map show fieldlist
fieldset = S.fromList $ zip fieldlist (repeat hc) fieldset = S.fromList $ zip fieldlist (repeat hc)
fieldlist = map privDataField srclist
hc = asHostContext c hc = asHostContext c
addPrivDataField :: (PrivDataField, HostContext) -> Property addPrivDataField :: (PrivDataField, HostContext) -> Property

View File

@ -63,9 +63,11 @@ installed = Apt.installed ["docker.io"]
configured :: Property configured :: Property
configured = prop `requires` installed configured = prop `requires` installed
where where
prop = withPrivData DockerAuthentication anyContext $ \getcfg -> prop = withPrivData src anyContext $ \getcfg ->
property "docker configured" $ getcfg $ \cfg -> ensureProperty $ property "docker configured" $ getcfg $ \cfg -> ensureProperty $
"/root/.dockercfg" `File.hasContent` (lines cfg) "/root/.dockercfg" `File.hasContent` (lines cfg)
src = PrivDataSourceFileFromCommand DockerAuthentication
"/root/.dockercfg" "docker login"
-- | A short descriptive name for a container. -- | A short descriptive name for a container.
-- Should not contain whitespace or other unusual characters, -- Should not contain whitespace or other unusual characters,

View File

@ -29,7 +29,7 @@ hasPrivContentExposed = hasPrivContent' writeFile
hasPrivContent' :: IsContext c => (String -> FilePath -> IO ()) -> FilePath -> c -> Property hasPrivContent' :: IsContext c => (String -> FilePath -> IO ()) -> FilePath -> c -> Property
hasPrivContent' writer f context = hasPrivContent' writer f context =
withPrivData (PrivFile f) context $ \getcontent -> withPrivData (PrivDataSourceFile (PrivFile f) f) context $ \getcontent ->
property desc $ getcontent $ \privcontent -> property desc $ getcontent $ \privcontent ->
ensureProperty $ fileProperty' writer desc ensureProperty $ fileProperty' writer desc
(\_oldcontent -> lines privcontent) f (\_oldcontent -> lines privcontent) f

View File

@ -28,13 +28,14 @@ keyImported (GpgKeyId keyid) user = flagFile' prop genflag
genflag = do genflag = do
d <- dotDir user d <- dotDir user
return $ d </> ".propellor-imported-keyid-" ++ keyid return $ d </> ".propellor-imported-keyid-" ++ keyid
prop = withPrivData GpgKey (Context keyid) $ \getkey -> prop = withPrivData src (Context keyid) $ \getkey ->
property desc $ getkey $ \key -> makeChange $ property desc $ getkey $ \key -> makeChange $
withHandle StdinHandle createProcessSuccess withHandle StdinHandle createProcessSuccess
(proc "su" ["-c", "gpg --import", user]) $ \h -> do (proc "su" ["-c", "gpg --import", user]) $ \h -> do
fileEncoding h fileEncoding h
hPutStr h key hPutStr h key
hClose h hClose h
src = PrivDataSource GpgKey "Either a gpg public key, exported with gpg --export -a, or a gpg private key, exported with gpg --export-secret-key -a"
dotDir :: UserName -> IO FilePath dotDir :: UserName -> IO FilePath
dotDir user = do dotDir user = do

View File

@ -90,8 +90,8 @@ hostKeys ctx = propertyList "known ssh host keys"
-- | Sets a single ssh host key from the privdata. -- | Sets a single ssh host key from the privdata.
hostKey :: IsContext c => SshKeyType -> c -> Property hostKey :: IsContext c => SshKeyType -> c -> Property
hostKey keytype context = combineProperties desc hostKey keytype context = combineProperties desc
[ installkey (SshPubKey keytype "") (install writeFile ".pub") [ installkey (keysrc ".pub" (SshPubKey keytype "")) (install writeFile ".pub")
, installkey (SshPrivKey keytype "") (install writeFileProtected "") , installkey (keysrc "" (SshPrivKey keytype "")) (install writeFileProtected "")
] ]
`onChange` restarted `onChange` restarted
where where
@ -104,6 +104,8 @@ hostKey keytype context = combineProperties desc
if s == key if s == key
then noChange then noChange
else makeChange $ writer f key else makeChange $ writer f key
keysrc ext field = PrivDataSourceFileFromCommand field ("sshkey"++ext)
("ssh-keygen -t " ++ sshKeyTypeParam keytype ++ " -f sshkey")
-- | Sets up a user with a ssh private key and public key pair from the -- | Sets up a user with a ssh private key and public key pair from the
-- PrivData. -- PrivData.

View File

@ -46,8 +46,13 @@ hasPassword user = hasPassword' user hostContext
hasPassword' :: IsContext c => UserName -> c -> Property hasPassword' :: IsContext c => UserName -> c -> Property
hasPassword' user context = go `requires` shadowConfig True hasPassword' user context = go `requires` shadowConfig True
where where
go = withSomePrivData [CryptPassword user, Password user] context $ go = withSomePrivData srcs context $
property (user ++ " has password") . setPassword property (user ++ " has password") . setPassword
srcs =
[ PrivDataSource (CryptPassword user)
"a crypt(3)ed password, which can be generated by, for example: perl -e 'print crypt(shift, q{$6$}.shift)' 'somepassword' 'somesalt'"
, PrivDataSource (Password user) ("a password for " ++ user)
]
setPassword :: (((PrivDataField, PrivData) -> Propellor Result) -> Propellor Result) -> Propellor Result setPassword :: (((PrivDataField, PrivData) -> Propellor Result) -> Propellor Result) -> Propellor Result
setPassword getpassword = getpassword $ go setPassword getpassword = getpassword $ go

View File

@ -16,23 +16,31 @@ data PrivDataField
| GpgKey | GpgKey
deriving (Read, Show, Ord, Eq) deriving (Read, Show, Ord, Eq)
-- | Explains how the user can generate a particular PrivDataField. -- | Combines a PrivDataField with a description of how to generate
howtoMkPrivDataField :: PrivDataField -> String -- its value.
howtoMkPrivDataField fld = case fld of data PrivDataSource
DockerAuthentication -> "/root/.dockercfg" `genbycmd` "docker login" = PrivDataSourceFile PrivDataField FilePath
SshPubKey keytype _ -> forexample $ | PrivDataSourceFileFromCommand PrivDataField FilePath String
"sshkey.pub" `genbycmd` keygen keytype | PrivDataSource PrivDataField String
SshPrivKey keytype _ -> forexample $
"sshkey" `genbycmd` keygen keytype class IsPrivDataSource s where
SshAuthorizedKeys _ -> forexample "~/.ssh/id_rsa.pub" privDataField :: s -> PrivDataField
Password username -> "a password for " ++ username describePrivDataSource :: s -> Maybe String
CryptPassword _ -> "a crypt(3)ed password, which can be generated by, for example: perl -e 'print crypt(shift, q{$6$}.shift)' 'somepassword' 'somesalt'"
PrivFile f -> "file contents for " ++ f instance IsPrivDataSource PrivDataField where
GpgKey -> "Either a gpg public key, exported with gpg --export -a, or a gpg private key, exported with gpg --export-secret-key -a" privDataField = id
where describePrivDataSource _ = Nothing
genbycmd f cmd = f ++ " generated by running `" ++ cmd ++ "`"
keygen keytype = "ssh-keygen -t " ++ sshKeyTypeParam keytype ++ " -f sshkey" instance IsPrivDataSource PrivDataSource where
forexample s = "for example, " ++ s privDataField s = case s of
PrivDataSourceFile f _ -> f
PrivDataSourceFileFromCommand f _ _ -> f
PrivDataSource f _ -> f
describePrivDataSource s = Just $ case s of
PrivDataSourceFile _ f -> "< " ++ f
PrivDataSourceFileFromCommand _ f c ->
"< " ++ f ++ " (created by running, for example, `" ++ c ++ "` )"
PrivDataSource _ d -> "< (" ++ d ++ ")"
-- | A context in which a PrivDataField is used. -- | A context in which a PrivDataField is used.
-- --