kerry-0.1.1: src/Kerry/Packer.hs
{-# 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 [
]