cloudy-0.1.0.0: src/Cloudy/Cli/Scaleway.hs
{-# LANGUAGE OverloadedRecordDot #-}
module Cloudy.Cli.Scaleway where
import Cloudy.Cli.Utils (maybeOpt)
import Cloudy.InstanceSetup (builtInInstanceSetups)
import Cloudy.InstanceSetup.Types (InstanceSetup (..), InstanceSetupData (..))
import Control.Applicative (optional)
import Data.Text (Text, unpack)
import Options.Applicative (Parser, command, info, progDesc, hsubparser, strOption, long, short, metavar, option, help, value, showDefault, maybeReader, switch, auto, footerDoc, completeWith)
import Options.Applicative.Help (vsep, Doc)
import Data.String (IsString(fromString))
import Cloudy.Scaleway (allScalewayZones, zoneToText)
data ScalewayCliOpts
= ScalewayCreate ScalewayCreateCliOpts
| ScalewayListInstanceTypes ScalewayListInstanceTypesCliOpts
| ScalewayListImages ScalewayListImagesCliOpts
deriving stock Show
data ScalewayCreateCliOpts = ScalewayCreateCliOpts
{ zone :: Maybe Text
, instanceType :: Maybe Text
, volumeSizeGb :: Int
, imageId :: Maybe Text
, instanceSetup :: Maybe Text
}
deriving stock Show
data ScalewayListInstanceTypesCliOpts = ScalewayListInstanceTypesCliOpts
{ zone :: Maybe Text
}
deriving stock Show
data ScalewayListImagesCliOpts = ScalewayListImagesCliOpts
{ zone :: Maybe Text
, arch :: Text
, nameFilter :: Maybe Text
, allVersions :: Bool
}
deriving stock Show
scalewayCliOptsParser :: [InstanceSetup] -> Parser ScalewayCliOpts
scalewayCliOptsParser userInstanceSetups = hsubparser subParsers
where
subParsers = createCommand <> listInstanceTypesCommand <> listImagesCommand
createCommand =
command
"create"
( info
(ScalewayCreate <$> scalewayCreateCliOptsParser userInstanceSetups)
( progDesc "Create a new compute instance in Scaleway" <>
(footerDoc . Just $
-- TODO: do this better
vsep
( [ "You can use the --instance-setup option to configure which \
\instance setup script is used to setup the instance after \
\boot. The instance setup scripts generally have a \
\`cloud-init` section, which specifies the actual cloud-init \
\setup to use."
, ""
, "Default instance-setup scripts builtin to Cloudy:"
, ""
] <>
fmap instanceSetupToDoc builtInInstanceSetups <>
[ ""
, "User-defined instance-setup scripts in ~/.config/cloudy/instance-setups/:"
, ""
] <>
case userInstanceSetups of
[] -> ["(none exist)"]
_ -> fmap instanceSetupToDoc userInstanceSetups
)
)
)
)
listInstanceTypesCommand =
command
"list-instance-types"
( info
(fmap ScalewayListInstanceTypes scalewayListInstanceTypesCliOptsParser)
(progDesc "List all instance types in Scaleway")
)
listImagesCommand =
command
"list-images"
( info
(fmap ScalewayListImages scalewayListImagesCliOptsParser)
(progDesc "List available images in Scaleway")
)
instanceSetupToDoc :: InstanceSetup -> Doc
instanceSetupToDoc instanceSetup =
" - " <> fromString (unpack instanceSetup.name) <>
" -- " <> fromString (unpack instanceSetup.instanceSetupData.shortDescription)
scalewayCreateCliOptsParser :: [InstanceSetup] -> Parser ScalewayCreateCliOpts
scalewayCreateCliOptsParser userInstanceSetups =
ScalewayCreateCliOpts
<$> zoneParser
<*> instanceTypeParser
<*> volumeSizeGbParser
<*> imageIdParser
<*> instanceSetupParser userInstanceSetups
scalewayListInstanceTypesCliOptsParser :: Parser ScalewayListInstanceTypesCliOpts
scalewayListInstanceTypesCliOptsParser = ScalewayListInstanceTypesCliOpts <$> zoneParser
scalewayListImagesCliOptsParser :: Parser ScalewayListImagesCliOpts
scalewayListImagesCliOptsParser =
ScalewayListImagesCliOpts
<$> zoneParser
<*> archParser
<*> nameFilterParser
<*> allVersionsParser
zoneParser :: Parser (Maybe Text)
zoneParser =
maybeOpt
"Scaleway zone in which to create the new instance"
"nl-ams-1"
strOption
( long "zone" <>
short 'z' <>
metavar "ZONE" <>
completeWith (unpack . zoneToText <$> allScalewayZones)
)
instanceTypeParser :: Parser (Maybe Text)
instanceTypeParser =
maybeOpt
"Scaleway instance type (use `cloudy scaleway list-instance-types` command to get list of all instance types)"
"PLAY2-NANO"
strOption
( long "instance-type" <>
short 'c' <>
metavar "INSTANCE_TYPE"
)
archParser :: Parser Text
archParser =
option
(maybeReader archReader)
( long "arch" <>
short 'a' <>
metavar "ARCH" <>
help "Architecture of image. Possiblities: \"x86_64\", \"arm\", or \"arm64\"" <>
value "x86_64" <>
showDefault <>
completeWith ["x86_64", "arm", "arm64"]
)
where
archReader :: String -> Maybe Text
archReader = \case
"x86_64" -> Just "x86_64"
"arm" -> Just "arm"
"arm64" -> Just "arm64"
_ -> Nothing
nameFilterParser :: Parser (Maybe Text)
nameFilterParser =
optional $
strOption
( long "name-filter" <>
short 'n' <>
metavar "NAME_FILTER" <>
help "Only show images whose name contains this value, case-insensitive (default: no filter)"
)
allVersionsParser :: Parser Bool
allVersionsParser =
switch
( long "all-versions" <>
short 'a' <>
help "List all versions of each image. By default, only show the latest version for each image name."
)
volumeSizeGbParser :: Parser Int
volumeSizeGbParser =
option
auto
( long "volume-size" <>
short 's' <>
metavar "VOLUME_SIZE" <>
help "Size of the root volume in GBs" <>
value 50 <>
showDefault
)
imageIdParser :: Parser (Maybe Text)
imageIdParser =
maybeOpt
"Scaleway image ID (use `cloudy scaleway list-images` command to get list of possible image IDs). Also can be image label, like \"ubuntu_noble\" (TODO: implement market api to return list of possible labels)"
"ubuntu_noble"
strOption
( long "image-id" <>
short 'i' <>
metavar "IMAGE_ID"
)
instanceSetupParser :: [InstanceSetup] -> Parser (Maybe Text)
instanceSetupParser userInstanceSetups =
optional $
strOption
( long "instance-setup" <>
short 't' <>
metavar "INSTANCE_SETUP" <>
help "Name of the instance-setup to use when booting the image. (default: do no instance setup)" <>
completeWith
(fmap (\instSetup -> unpack instSetup.name) (userInstanceSetups <> builtInInstanceSetups))
)