{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module Kerry.Packer ( -- * Core data types -- ** Packer Packer(..) -- *** User variables , UserVariable(..) -- *** Builders , Builder(..) , BuilderType(..) -- *** Provisioners , Provisioner(..) , provisioner , ProvisionerType(..) -- *** PostProcessors , PostProcessor(..) -- *** Communicators , Communicator(..) , SSHCommunicator(..) , defaultSSHCommunicator -- * Render , renderPacker -- * Serialization , fromPacker , fromBuilder , fromBuilderType , fromUserVariable , fromProvisioner , fromPostProcessor ) where import Data.Aeson ((.=)) import qualified Data.Aeson as A import qualified Data.Aeson.Types as A import qualified Kerry.Builder.AmazonEC2 as AmazonEC2 import qualified Kerry.Provisioner.File as File import qualified Kerry.Provisioner.Shell as Shell import Kerry.Internal.Prelude import Kerry.Internal.Serial (list, listToObject, prettyAsTextWith, (.=?), fromMapWith, fromMap) data UserVariable = UserVariable Text Text deriving (Eq, Ord, Show) -- | -- Builders -- -- See the following for more information -- - https://www.packer.io/docs/templates/builders.html -- - https://www.packer.io/docs/builders/index.html -- data Builder = Builder { builderType :: BuilderType , builderName :: Maybe Text , builderCommunicator :: Communicator } deriving (Eq, Ord, Show) -- | Concrete 'BuilderType' data BuilderType = AmazonEBSBuilder (AmazonEC2.AWS AmazonEC2.EBS) deriving (Eq, Ord, Show) -- | -- Provisioner -- -- See the following for more information: -- - https://www.packer.io/docs/templates/provisioners.html -- - https://www.packer.io/docs/provisioners/index.html -- data Provisioner = Provisioner { provisionerType :: ProvisionerType , provisionerOnly :: [Text] , provisionerExcept :: [Text] , provisionerPauseBefore :: Maybe Text , provisionerTimeout :: Maybe Text , provisionerOverride :: Maybe (Map Text (Map Text Text)) } deriving (Eq, Ord, Show) -- | Basic 'Provisioner' provisioner :: ProvisionerType -> Provisioner provisioner pt = Provisioner { provisionerType = pt , provisionerOnly = [] , provisionerExcept = [] , provisionerPauseBefore = Nothing , provisionerTimeout = Nothing , provisionerOverride = Nothing } -- | Concrete 'ProvisionerType' data ProvisionerType = ShellProvisioner Shell.Shell | FileProvisioner File.File deriving (Eq, Ord, Show) data PostProcessor = PostProcessor deriving (Eq, Ord, Show) -- | -- Packer -- -- A concrete representation for configuring the various components of Packer. -- data Packer = Packer { variables :: [UserVariable] , builders :: [Builder] , provisioners :: [Provisioner] , postProcessors :: [PostProcessor] } deriving (Eq, Ord, Show) -- | -- Communicator -- -- See the following for more information: -- - https://www.packer.io/docs/templates/communicator.html -- - https://www.packer.io/docs/provisioners/index.html -- data Communicator = -- | No communicator will be used. If this is set, most provisioners also can't be used. None -- | An SSH connection will be established to the machine. This is usually the default. | SSH SSHCommunicator -- | A WinRM connection will be established. | WinRm deriving (Eq, Ord, Show) -- | -- 'ssh' communicator -- -- https://www.packer.io/docs/templates/communicator.html#ssh -- data SSHCommunicator = SSHCommunicator { sshUsername :: Text -- ^ 'ssh_username' - The username to connect to SSH with. Required if using SSH. , sshPty :: Bool -- ^ 'ssh_pty' - If true, a PTY will be requested for the SSH connection. This defaults to false. , sshTimeout :: Int -- ^ 'ssh_timeout' (string) - The time to wait for SSH to become available. Packer uses this to determine when the machine has booted so this is usually quite long. Example value: 10. -- 'ssh_agent_auth' (boolean) - If true, the local SSH agent will be used to authenticate connections to the remote host. Defaults to false. -- 'ssh_bastion_agent_auth' (boolean) - If true, the local SSH agent will be used to authenticate with the bastion host. Defaults to false. -- 'ssh_bastion_host' (string) - A bastion host to use for the actual SSH connection. -- 'ssh_bastion_password' (string) - The password to use to authenticate with the bastion host. -- 'ssh_bastion_port' (number) - The port of the bastion host. Defaults to 22. -- 'ssh_bastion_private_key_file' (string) - Path to a PEM encoded private key file to use to authenticate with the bastion host. The ~ can be used in path and will be expanded to the home directory of current user. -- 'ssh_bastion_username' (string) - The username to connect to the bastion host. -- 'ssh_clear_authorized_keys' (boolean) - If true, Packer will attempt to remove its temporary key from ~/.ssh/authorized_keys and /root/.ssh/authorized_keys. This is a mostly cosmetic option, since Packer will delete the temporary private key from the host system regardless of whether this is set to true (unless the user has set the -debug flag). Defaults to "false"; currently only works on guests with sed installed. -- 'ssh_disable_agent_forwarding' (boolean) - If true, SSH agent forwarding will be disabled. Defaults to false. -- 'ssh_file_transfer_method' (scp or sftp) - How to transfer files, Secure copy (default) or SSH File Transfer Protocol. -- 'ssh_handshake_attempts' (number) - The number of handshakes to attempt with SSH once it can connect. This defaults to 10. -- 'ssh_host' (string) - The address to SSH to. This usually is automatically configured by the builder. -- 'ssh_keep_alive_interval' (string) - How often to send "keep alive" messages to the server. Set to a negative value (-1s) to disable. Example value: 10s. Defaults to 5s. -- 'ssh_password' (string) - A plaintext password to use to authenticate with SSH. -- 'ssh_port' (number) - The port to connect to SSH. This defaults to 22. -- 'ssh_private_key_file' (string) - Path to a PEM encoded private key file to use to authenticate with SSH. The ~ can be used in path and will be expanded to the home directory of current user. -- 'ssh_proxy_host' (string) - A SOCKS proxy host to use for SSH connection -- 'ssh_proxy_password' (string) - The password to use to authenticate with the proxy server. Optional. -- 'ssh_proxy_port' (number) - A port of the SOCKS proxy. Defaults to 1080. -- 'ssh_proxy_username' (string) - The username to authenticate with the proxy server. Optional. -- 'ssh_read_write_timeout' (string) - The amount of time to wait for a remote command to end. This might be useful if, for example, packer hangs on a connection after a reboot. Example: 5m. Disabled by default. } deriving (Eq, Ord, Show) -- | -- A minimal default @ssh@ communicator where only the @username@ -- needs to be specified -- defaultSSHCommunicator :: Text -> SSHCommunicator defaultSSHCommunicator username = SSHCommunicator { sshUsername = username , sshPty = True , sshTimeout = 10 } -- | Render an 'Packer' to 'Text' renderPacker :: Packer -> Text renderPacker = prettyAsTextWith fromPacker -- | Packer serialization fromPacker :: Packer -> A.Value fromPacker p = A.object $ join [ "variables" .=? (listToObject <$> list (fromUserVariable <$> variables p)) , "builders" .=? list (fromBuilder <$> builders p) , "provisioners" .=? list (fromProvisioner <$> provisioners p) , "post-processors" .=? list (fromPostProcessor <$> postProcessors p) ] -- | Builder serialization fromBuilder :: Builder -> A.Value fromBuilder (Builder btype name comm) = A.object $ join [ fromBuilderType btype , "name" .=? name , fromCommunicator comm ] -- | BuilderType serialization fromBuilderType :: BuilderType -> [A.Pair] fromBuilderType = \case AmazonEBSBuilder aws -> AmazonEC2.fromAWS AmazonEC2.fromEBS aws -- | Communicator serialization fromCommunicator :: Communicator -> [A.Pair] fromCommunicator = \case None -> [] SSH (SSHCommunicator user pty timeout) -> [ "ssh_username" .= user , "ssh_pty" .= pty , "ssh_timeout" .= (show timeout <> "m") ] WinRm -> [] -- | UserVariable serialization fromUserVariable :: UserVariable -> A.Pair fromUserVariable (UserVariable k v) = k .= v -- | Provisioner serialization fromProvisioner :: Provisioner -> A.Value fromProvisioner p = A.object $ join [ fromProvisionerType (provisionerType p) , "only" .=? list (provisionerOnly p) , "except" .=? list (provisionerExcept p) , "pause_before" .=? (provisionerPauseBefore p) , "timeout" .=? (provisionerTimeout p) , "override" .=? (fromMapWith fromMap <$> provisionerOverride p) ] fromProvisionerType :: ProvisionerType -> [A.Pair] fromProvisionerType = \case ShellProvisioner shell -> Shell.fromShell shell FileProvisioner file -> File.fromFile file -- | PostProcessor serialization fromPostProcessor :: PostProcessor -> A.Value fromPostProcessor _ = A.object $ join [ ]