packages feed

hdo 0.2 → 0.3

raw patch · 23 files changed

+947/−231 lines, 23 filesdep +case-insensitivedep +http-conduitdep +parsecdep −wreqdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: case-insensitive, http-conduit, parsec

Dependencies removed: wreq

Dependency ranges changed: base

API changes (from Hackage documentation)

- Network.DO.Commands: CoDO :: (m [Key], k) -> (m [Size], k) -> (m [Image], k) -> (m [Region], k) -> CoDO m k
- Network.DO.Commands: ListImages :: ([Image] -> a) -> DO a
- Network.DO.Commands: ListKeys :: ([Key] -> a) -> DO a
- Network.DO.Commands: ListRegions :: ([Region] -> a) -> DO a
- Network.DO.Commands: ListSizes :: ([Size] -> a) -> DO a
- Network.DO.Commands: [listImagesH] :: CoDO m k -> (m [Image], k)
- Network.DO.Commands: [listKeysH] :: CoDO m k -> (m [Key], k)
- Network.DO.Commands: [listRegionsH] :: CoDO m k -> (m [Region], k)
- Network.DO.Commands: [listSizesH] :: CoDO m k -> (m [Size], k)
- Network.DO.Commands: data CoDO m k
- Network.DO.Commands: data DO a
- Network.DO.Commands: instance GHC.Base.Functor (Network.DO.Commands.CoDO m)
- Network.DO.Commands: instance GHC.Base.Functor Network.DO.Commands.DO
- Network.DO.Commands: instance GHC.Base.Monad m => Network.DO.Pairing.PairingM (Network.DO.Commands.CoDO m) Network.DO.Commands.DO m
- Network.DO.Commands: listImages :: DO [Image]
- Network.DO.Commands: listKeys :: DO [Key]
- Network.DO.Commands: listRegions :: DO [Region]
- Network.DO.Commands: listSizes :: DO [Size]
- Network.DO.Commands: type CoDOT m = CofreeT (CoDO m)
- Network.DO.Commands: type DOT = FreeT DO
- Network.DO.Droplets.Commands: CoDropletCommands :: (m [Droplet], k) -> (BoxConfiguration -> (m (Result Droplet), k)) -> (Id -> (m (Maybe String), k)) -> (Id -> Action -> (m (Result ActionResult), k)) -> (Id -> Id -> (m (Result ActionResult), k)) -> (Id -> (m [Image], k)) -> (Droplet -> (m (Result ()), k)) -> (Id -> (m (Result Droplet), k)) -> CoDropletCommands m k
- Network.DO.Droplets.Commands: [actionDropletH] :: CoDropletCommands m k -> Id -> Action -> (m (Result ActionResult), k)
- Network.DO.Droplets.Commands: [consoleH] :: CoDropletCommands m k -> Droplet -> (m (Result ()), k)
- Network.DO.Droplets.Commands: [createDropletH] :: CoDropletCommands m k -> BoxConfiguration -> (m (Result Droplet), k)
- Network.DO.Droplets.Commands: [destroyDropletH] :: CoDropletCommands m k -> Id -> (m (Maybe String), k)
- Network.DO.Droplets.Commands: [getActionH] :: CoDropletCommands m k -> Id -> Id -> (m (Result ActionResult), k)
- Network.DO.Droplets.Commands: [listDropletsH] :: CoDropletCommands m k -> (m [Droplet], k)
- Network.DO.Droplets.Commands: [listSnapshotsH] :: CoDropletCommands m k -> Id -> (m [Image], k)
- Network.DO.Droplets.Commands: [showDropletH] :: CoDropletCommands m k -> Id -> (m (Result Droplet), k)
- Network.DO.Droplets.Commands: createDroplet :: BoxConfiguration -> DropletCommands (Result Droplet)
- Network.DO.Droplets.Commands: data CoDropletCommands m k
- Network.DO.Droplets.Commands: data DropletCommands a
- Network.DO.Droplets.Commands: destroyDroplet :: Id -> DropletCommands (Maybe String)
- Network.DO.Droplets.Commands: dropletAction :: Id -> Action -> DropletCommands (Result ActionResult)
- Network.DO.Droplets.Commands: dropletConsole :: Droplet -> DropletCommands (Result ())
- Network.DO.Droplets.Commands: getAction :: Id -> Id -> DropletCommands (Result ActionResult)
- Network.DO.Droplets.Commands: instance GHC.Base.Functor (Network.DO.Droplets.Commands.CoDropletCommands m)
- Network.DO.Droplets.Commands: instance GHC.Base.Functor Network.DO.Droplets.Commands.DropletCommands
- Network.DO.Droplets.Commands: instance GHC.Base.Monad m => Network.DO.Pairing.PairingM (Network.DO.Droplets.Commands.CoDropletCommands m) Network.DO.Droplets.Commands.DropletCommands m
- Network.DO.Droplets.Commands: listDropletSnapshots :: Id -> DropletCommands [Image]
- Network.DO.Droplets.Commands: listDroplets :: DropletCommands [Droplet]
- Network.DO.Droplets.Commands: showDroplet :: Id -> DropletCommands (Result Droplet)
- Network.DO.Droplets.Commands: type CoDropletCommandsT m = CofreeT (CoDropletCommands m)
- Network.DO.Droplets.Commands: type DropletCommandsT = FreeT DropletCommands
- Network.DO.Droplets.Net: dropletCommandsInterpreter :: (MonadIO m, ComonadEnv ToolConfiguration w) => w a -> CoDropletCommands (RESTT m) (w a)
- Network.DO.Droplets.Net: instance Network.DO.Net.Common.Listable Network.DO.Types.Droplet
- Network.DO.Droplets.Utils: findByIdOrName :: String -> [Droplet] -> [Droplet]
- Network.DO.Droplets.Utils: publicIP :: Droplet -> Maybe IP
- Network.DO.Names: generateName :: IO String
- Network.DO.Net: instance Network.DO.Net.Common.Listable Network.DO.Types.Image
- Network.DO.Net: instance Network.DO.Net.Common.Listable Network.DO.Types.Key
- Network.DO.Net: instance Network.DO.Net.Common.Listable Network.DO.Types.Region
- Network.DO.Net: instance Network.DO.Net.Common.Listable Network.DO.Types.Size
- Network.DO.Net: mkDOClient :: (MonadIO m) => ToolConfiguration -> CofreeT (Product (CoDO (RESTT m)) (CoDropletCommands (RESTT m))) (Env ToolConfiguration) (RESTT m ())
- Network.DO.Net.Common: (</>) :: String -> String -> String
- Network.DO.Net.Common: apiVersion :: String
- Network.DO.Net.Common: authorisation :: String -> Options
- Network.DO.Net.Common: class Listable a
- Network.DO.Net.Common: listEndpoint :: Listable a => Proxy a -> String
- Network.DO.Net.Common: listField :: Listable a => Proxy a -> Text
- Network.DO.Net.Common: queryList :: (ComonadEnv ToolConfiguration w, Monad m, Listable b, FromJSON b) => Proxy b -> w a -> (RESTT m [b], w a)
- Network.DO.Net.Common: rootURI :: String
- Network.DO.Net.Common: toList :: (FromJSON a) => Text -> Value -> [a]
- Network.DO.Net.Common: toURI :: String -> URI
- Network.DO.Pairing: class (Functor f, Functor g) => Pairing f g
- Network.DO.Pairing: class (Functor f, Functor g, Monad m) => PairingM f g m
- Network.DO.Pairing: injl :: (Monad m, Functor f, Functor g) => f a -> FreeT (Sum f g) m a
- Network.DO.Pairing: injr :: (Monad m, Functor f, Functor g) => g a -> FreeT (Sum f g) m a
- Network.DO.Pairing: instance (GHC.Base.Monad m, Network.DO.Pairing.PairingM f h m, Network.DO.Pairing.PairingM g k m) => Network.DO.Pairing.PairingM (Data.Functor.Sum.Sum f g) (Data.Functor.Product.Product h k) m
- Network.DO.Pairing: instance (GHC.Base.Monad m, Network.DO.Pairing.PairingM h f m, Network.DO.Pairing.PairingM k g m) => Network.DO.Pairing.PairingM (Data.Functor.Product.Product h k) (Data.Functor.Sum.Sum f g) m
- Network.DO.Pairing: instance GHC.Base.Monad m => Network.DO.Pairing.PairingM ((,) (m a)) ((->) a) m
- Network.DO.Pairing: instance Network.DO.Pairing.Pairing ((,) a) ((->) a)
- Network.DO.Pairing: instance Network.DO.Pairing.Pairing ((->) a) ((,) a)
- Network.DO.Pairing: instance Network.DO.Pairing.Pairing Data.Functor.Identity.Identity Data.Functor.Identity.Identity
- Network.DO.Pairing: pair :: Pairing f g => (a -> b -> r) -> f a -> g b -> r
- Network.DO.Pairing: pairEffect :: (Pairing f g, Comonad w, Monad m) => (a -> b -> r) -> CofreeT f w a -> FreeT g m b -> m r
- Network.DO.Pairing: pairEffect' :: (Pairing f g, Comonad w, Monad m) => (a -> b -> m r) -> CofreeT f w a -> FreeT g m b -> m r
- Network.DO.Pairing: pairEffectM :: (PairingM f g m, Comonad w) => (a -> b -> m r) -> CofreeT f w (m a) -> FreeT g m b -> m r
- Network.DO.Pairing: pairM :: PairingM f g m => (a -> b -> m r) -> f a -> g b -> m r
- Network.DO.Pretty: class (Show a) => Pretty a where pretty = text . show
- Network.DO.Pretty: instance (Network.DO.Pretty.Pretty a, Network.DO.Pretty.Pretty b) => Network.DO.Pretty.Pretty (Data.Either.Either a b)
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty ()
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty (Network.DO.Types.Bytes Network.DO.Types.Giga)
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty (Network.DO.Types.Bytes Network.DO.Types.Mega)
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty (Network.DO.Types.Network a)
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Data.IP.Addr.IP
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty GHC.Types.Char
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.ActionResult
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Date
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Droplet
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Error
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Image
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Key
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.NetType
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Networks
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Region
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Size
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.SizeSlug
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.Status
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty Network.DO.Types.TransferRate
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty a => Network.DO.Pretty.Pretty (GHC.Base.Maybe a)
- Network.DO.Pretty: instance Network.DO.Pretty.Pretty a => Network.DO.Pretty.Pretty [a]
- Network.DO.Pretty: outputResult :: (Pretty a, MonadIO m) => a -> m ()
- Network.DO.Pretty: pretty :: Pretty a => a -> Doc
- Network.DO.Types: A :: DNSType
- Network.DO.Types: AAAA :: DNSType
- Network.DO.Types: ActionResult :: Id -> ActionStatus -> ActionType -> Maybe Date -> Maybe Date -> Id -> String -> Region -> ActionResult
- Network.DO.Types: Active :: Status
- Network.DO.Types: Archive :: Status
- Network.DO.Types: Backup :: ImageType
- Network.DO.Types: BoxConfiguration :: String -> Region -> SizeSlug -> ImageSlug -> [KeyId] -> Bool -> BoxConfiguration
- Network.DO.Types: Bytes :: Int -> Bytes a
- Network.DO.Types: CNAME :: DNSType
- Network.DO.Types: Completed :: ActionStatus
- Network.DO.Types: CreateSnapshot :: String -> Action
- Network.DO.Types: Date :: UTCTime -> Date
- Network.DO.Types: DoPowerOff :: Action
- Network.DO.Types: DoPowerOn :: Action
- Network.DO.Types: Domain :: String -> Int -> String -> Domain
- Network.DO.Types: DomainRecord :: Id -> DNSType -> String -> String -> Maybe Double -> Maybe Int -> Maybe Double -> DomainRecord
- Network.DO.Types: Droplet :: Id -> String -> Bytes Mega -> Int -> Bytes Giga -> Bool -> Date -> Status -> [Id] -> [Id] -> Region -> SizeSlug -> Networks -> Droplet
- Network.DO.Types: Error :: String -> Error
- Network.DO.Types: Errored :: ActionStatus
- Network.DO.Types: G1 :: SizeSlug
- Network.DO.Types: G16 :: SizeSlug
- Network.DO.Types: G2 :: SizeSlug
- Network.DO.Types: G32 :: SizeSlug
- Network.DO.Types: G4 :: SizeSlug
- Network.DO.Types: G48 :: SizeSlug
- Network.DO.Types: G64 :: SizeSlug
- Network.DO.Types: G8 :: SizeSlug
- Network.DO.Types: G96 :: SizeSlug
- Network.DO.Types: Image :: Id -> String -> String -> Maybe Slug -> Bool -> [Region] -> Bytes Giga -> Date -> ImageType -> Image
- Network.DO.Types: InProgress :: ActionStatus
- Network.DO.Types: Key :: Id -> String -> String -> String -> Key
- Network.DO.Types: M512 :: SizeSlug
- Network.DO.Types: MX :: DNSType
- Network.DO.Types: MakeSnapshot :: ActionType
- Network.DO.Types: NS :: DNSType
- Network.DO.Types: NetworkV4 :: IP -> IP -> IP -> NetType -> Network a
- Network.DO.Types: NetworkV6 :: IP -> Int -> IP -> NetType -> Network a
- Network.DO.Types: Networks :: [Network V4] -> [Network V6] -> Networks
- Network.DO.Types: New :: Status
- Network.DO.Types: NoNetworks :: Networks
- Network.DO.Types: NoRegion :: Region
- Network.DO.Types: Off :: Status
- Network.DO.Types: PTR :: DNSType
- Network.DO.Types: PowerOff :: ActionType
- Network.DO.Types: PowerOn :: ActionType
- Network.DO.Types: Private :: NetType
- Network.DO.Types: Public :: NetType
- Network.DO.Types: Region :: String -> Slug -> [SizeSlug] -> Bool -> Region
- Network.DO.Types: RegionSlug :: Slug -> Region
- Network.DO.Types: SRV :: DNSType
- Network.DO.Types: Size :: SizeSlug -> Bytes Mega -> Int -> Bytes Giga -> TransferRate -> Price -> Price -> [Region] -> Bool -> Size
- Network.DO.Types: Snapshot :: ImageType
- Network.DO.Types: TXT :: DNSType
- Network.DO.Types: Temporary :: ImageType
- Network.DO.Types: Tool :: Maybe URI -> Maybe AuthToken -> Bool -> ToolConfiguration
- Network.DO.Types: [actionCompletedAt] :: ActionResult -> Maybe Date
- Network.DO.Types: [actionId] :: ActionResult -> Id
- Network.DO.Types: [actionRegionSlug] :: ActionResult -> Region
- Network.DO.Types: [actionResourceId] :: ActionResult -> Id
- Network.DO.Types: [actionResourceType] :: ActionResult -> String
- Network.DO.Types: [actionStartedAt] :: ActionResult -> Maybe Date
- Network.DO.Types: [actionStatus] :: ActionResult -> ActionStatus
- Network.DO.Types: [actionType] :: ActionResult -> ActionType
- Network.DO.Types: [authToken] :: ToolConfiguration -> Maybe AuthToken
- Network.DO.Types: [backgroundCreate] :: BoxConfiguration -> Bool
- Network.DO.Types: [backup_ids] :: Droplet -> [Id]
- Network.DO.Types: [boxRegion] :: BoxConfiguration -> Region
- Network.DO.Types: [bytesSize] :: Bytes a -> Int
- Network.DO.Types: [configImageSlug] :: BoxConfiguration -> ImageSlug
- Network.DO.Types: [configName] :: BoxConfiguration -> String
- Network.DO.Types: [created_at] :: Droplet -> Date
- Network.DO.Types: [disk] :: Droplet -> Bytes Giga
- Network.DO.Types: [distribution] :: Image -> String
- Network.DO.Types: [domainName] :: Domain -> String
- Network.DO.Types: [domainTTL] :: Domain -> Int
- Network.DO.Types: [dropletId] :: Droplet -> Id
- Network.DO.Types: [gateway] :: Network a -> IP
- Network.DO.Types: [imageId] :: Image -> Id
- Network.DO.Types: [imageName] :: Image -> String
- Network.DO.Types: [imageRegions] :: Image -> [Region]
- Network.DO.Types: [imageSlug] :: Image -> Maybe Slug
- Network.DO.Types: [imageType] :: Image -> ImageType
- Network.DO.Types: [image_created_at] :: Image -> Date
- Network.DO.Types: [ip_address] :: Network a -> IP
- Network.DO.Types: [keyFingerprint] :: Key -> String
- Network.DO.Types: [keyId] :: Key -> Id
- Network.DO.Types: [keyName] :: Key -> String
- Network.DO.Types: [keys] :: BoxConfiguration -> [KeyId]
- Network.DO.Types: [locked] :: Droplet -> Bool
- Network.DO.Types: [memory] :: Droplet -> Bytes Mega
- Network.DO.Types: [min_disk_size] :: Image -> Bytes Giga
- Network.DO.Types: [msg] :: Error -> String
- Network.DO.Types: [name] :: Droplet -> String
- Network.DO.Types: [netType] :: Network a -> NetType
- Network.DO.Types: [netmask] :: Network a -> IP
- Network.DO.Types: [netmask_v6] :: Network a -> Int
- Network.DO.Types: [networks] :: Droplet -> Networks
- Network.DO.Types: [publicImage] :: Image -> Bool
- Network.DO.Types: [publicKey] :: Key -> String
- Network.DO.Types: [quiet] :: ToolConfiguration -> Bool
- Network.DO.Types: [recordData] :: DomainRecord -> String
- Network.DO.Types: [recordId] :: DomainRecord -> Id
- Network.DO.Types: [recordName] :: DomainRecord -> String
- Network.DO.Types: [recordPort] :: DomainRecord -> Maybe Int
- Network.DO.Types: [recordPriority] :: DomainRecord -> Maybe Double
- Network.DO.Types: [recordType] :: DomainRecord -> DNSType
- Network.DO.Types: [recordWeight] :: DomainRecord -> Maybe Double
- Network.DO.Types: [regionAvailable] :: Region -> Bool
- Network.DO.Types: [regionName] :: Region -> String
- Network.DO.Types: [regionSizes] :: Region -> [SizeSlug]
- Network.DO.Types: [regionSlug] :: Region -> Slug
- Network.DO.Types: [region] :: Droplet -> Region
- Network.DO.Types: [size] :: BoxConfiguration -> SizeSlug
- Network.DO.Types: [size_slug] :: Droplet -> SizeSlug
- Network.DO.Types: [slackUri] :: ToolConfiguration -> Maybe URI
- Network.DO.Types: [snapshot_ids] :: Droplet -> [Id]
- Network.DO.Types: [status] :: Droplet -> Status
- Network.DO.Types: [szAvailable] :: Size -> Bool
- Network.DO.Types: [szDisk] :: Size -> Bytes Giga
- Network.DO.Types: [szMemory] :: Size -> Bytes Mega
- Network.DO.Types: [szPrice_Hourly] :: Size -> Price
- Network.DO.Types: [szPrice_Monthly] :: Size -> Price
- Network.DO.Types: [szRegions] :: Size -> [Region]
- Network.DO.Types: [szSlug] :: Size -> SizeSlug
- Network.DO.Types: [szTransfer] :: Size -> TransferRate
- Network.DO.Types: [szVcpus] :: Size -> Int
- Network.DO.Types: [theDate] :: Date -> UTCTime
- Network.DO.Types: [v4] :: Networks -> [Network V4]
- Network.DO.Types: [v6] :: Networks -> [Network V6]
- Network.DO.Types: [vcpus] :: Droplet -> Int
- Network.DO.Types: [zone_file] :: Domain -> String
- Network.DO.Types: data Action
- Network.DO.Types: data ActionResult
- Network.DO.Types: data ActionStatus
- Network.DO.Types: data ActionType
- Network.DO.Types: data BoxConfiguration
- Network.DO.Types: data DNSType
- Network.DO.Types: data Domain
- Network.DO.Types: data DomainRecord
- Network.DO.Types: data Droplet
- Network.DO.Types: data Giga
- Network.DO.Types: data Image
- Network.DO.Types: data ImageType
- Network.DO.Types: data Key
- Network.DO.Types: data Mega
- Network.DO.Types: data NetType
- Network.DO.Types: data Network a
- Network.DO.Types: data Networks
- Network.DO.Types: data Region
- Network.DO.Types: data Size
- Network.DO.Types: data SizeSlug
- Network.DO.Types: data Status
- Network.DO.Types: data ToolConfiguration
- Network.DO.Types: data V4
- Network.DO.Types: data V6
- Network.DO.Types: defaultImage :: ImageSlug
- Network.DO.Types: error :: String -> Result a
- Network.DO.Types: failParse :: (Show a1, Monad m) => a1 -> m a
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.DO.Types.Bytes Network.DO.Types.Giga)
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.DO.Types.Bytes Network.DO.Types.Mega)
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.DO.Types.Network Network.DO.Types.V4)
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.DO.Types.Network Network.DO.Types.V6)
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.IP.Addr.IP
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.ActionResult
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.ActionStatus
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.ActionType
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.DNSType
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Date
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Domain
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.DomainRecord
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Droplet
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Image
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.ImageType
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Key
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.NetType
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Networks
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Region
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Size
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.SizeSlug
- Network.DO.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.DO.Types.Status
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.Action
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.ActionType
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.BoxConfiguration
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.DNSType
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.Region
- Network.DO.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.DO.Types.SizeSlug
- Network.DO.Types: instance Data.Default.Class.Default Network.DO.Types.ToolConfiguration
- Network.DO.Types: instance GHC.Classes.Eq Network.DO.Types.Error
- Network.DO.Types: instance GHC.Classes.Eq Network.DO.Types.NetType
- Network.DO.Types: instance GHC.Classes.Eq Network.DO.Types.SizeSlug
- Network.DO.Types: instance GHC.Classes.Eq Network.DO.Types.Status
- Network.DO.Types: instance GHC.Classes.Ord Network.DO.Types.SizeSlug
- Network.DO.Types: instance GHC.Enum.Enum Network.DO.Types.SizeSlug
- Network.DO.Types: instance GHC.Generics.Generic Network.DO.Types.DNSType
- Network.DO.Types: instance GHC.Generics.Generic Network.DO.Types.Networks
- Network.DO.Types: instance GHC.Read.Read Network.DO.Types.DNSType
- Network.DO.Types: instance GHC.Read.Read Network.DO.Types.Error
- Network.DO.Types: instance GHC.Read.Read Network.DO.Types.SizeSlug
- Network.DO.Types: instance GHC.Read.Read Network.DO.Types.ToolConfiguration
- Network.DO.Types: instance GHC.Show.Show (Network.DO.Types.Bytes a)
- Network.DO.Types: instance GHC.Show.Show (Network.DO.Types.Network a)
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Action
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.ActionResult
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.ActionStatus
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.ActionType
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.BoxConfiguration
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.DNSType
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Date
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Domain
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.DomainRecord
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Droplet
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Error
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Image
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.ImageType
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Key
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.NetType
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Networks
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Region
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Size
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.SizeSlug
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.Status
- Network.DO.Types: instance GHC.Show.Show Network.DO.Types.ToolConfiguration
- Network.DO.Types: jsonBytes :: Int -> Parser (Bytes a)
- Network.DO.Types: jsonNetwork :: (FromJSON a3, FromJSON a2, FromJSON a1, FromJSON a) => (a3 -> a2 -> a1 -> a -> b) -> Object -> Parser b
- Network.DO.Types: newtype Bytes a
- Network.DO.Types: newtype Date
- Network.DO.Types: newtype Error
- Network.DO.Types: sizeSlugs :: [String]
- Network.DO.Types: type AuthToken = String
- Network.DO.Types: type Id = Integer
- Network.DO.Types: type ImageSlug = String
- Network.DO.Types: type KeyId = Int
- Network.DO.Types: type Price = Double
- Network.DO.Types: type Result a = Either Error a
- Network.DO.Types: type Slug = String
- Network.DO.Types: type TransferRate = Double
- Network.DO.Types: type URI = String
- Network.REST: DeleteWith :: Options -> URI -> a -> REST a
- Network.REST: Get :: URI -> (Value -> a) -> REST a
- Network.REST: GetWith :: Options -> URI -> (Value -> a) -> REST a
- Network.REST: Post :: URI -> Value -> (Maybe Value -> a) -> REST a
- Network.REST: PostWith :: Options -> URI -> Value -> (Either String Value -> a) -> REST a
- Network.REST: WaitFor :: Int -> String -> a -> REST a
- Network.REST: data REST a
- Network.REST: deleteJSONWith :: (Monad m) => Options -> URI -> RESTT m ()
- Network.REST: getJSON :: (Monad m) => URI -> RESTT m Value
- Network.REST: getJSONWith :: (Monad m) => Options -> URI -> RESTT m Value
- Network.REST: postJSON :: (Monad m) => URI -> Value -> RESTT m (Maybe Value)
- Network.REST: postJSONWith :: (Monad m) => Options -> URI -> Value -> RESTT m (Either String Value)
- Network.REST: runWreq :: RESTT IO r -> IO r
- Network.REST: ssh :: (MonadIO m, Exception e) => [String] -> m (Either e ())
- Network.REST: type RESTT = FreeT REST
- Network.REST: waitFor :: (Monad m) => Int -> String -> RESTT m ()
+ Network.DO: A :: DNSType
+ Network.DO: AAAA :: DNSType
+ Network.DO: ActionResult :: Id -> ActionStatus -> result -> Maybe Date -> Maybe Date -> Id -> String -> Region -> ActionResult result
+ Network.DO: Active :: Status
+ Network.DO: Archive :: Status
+ Network.DO: Assign :: IPActionType
+ Network.DO: AssignIP :: Id -> IPAction
+ Network.DO: Backup :: ImageType
+ Network.DO: BoxConfiguration :: String -> Region -> SizeSlug -> ImageSlug -> [KeyId] -> Bool -> BoxConfiguration
+ Network.DO: Bytes :: Int -> Bytes a
+ Network.DO: CNAME :: DNSType
+ Network.DO: Completed :: ActionStatus
+ Network.DO: CreateSnapshot :: String -> Action
+ Network.DO: Date :: UTCTime -> Date
+ Network.DO: DoPowerOff :: Action
+ Network.DO: DoPowerOn :: Action
+ Network.DO: Domain :: DomainName -> Maybe Int -> Maybe String -> Domain
+ Network.DO: DomainConfig :: DomainName -> IP -> DomainConfig
+ Network.DO: DomainName :: String -> DomainName
+ Network.DO: DomainRecord :: Id -> DNSType -> String -> String -> Maybe Int -> Maybe Int -> Maybe Int -> DomainRecord
+ Network.DO: Droplet :: Id -> String -> Bytes Mega -> Int -> Bytes Giga -> Bool -> Date -> Status -> [Id] -> [Id] -> Region -> SizeSlug -> Networks -> Droplet
+ Network.DO: Error :: String -> Error
+ Network.DO: Errored :: ActionStatus
+ Network.DO: FloatingIP :: IP -> Maybe Droplet -> Region -> FloatingIP
+ Network.DO: G1 :: SizeSlug
+ Network.DO: G16 :: SizeSlug
+ Network.DO: G2 :: SizeSlug
+ Network.DO: G32 :: SizeSlug
+ Network.DO: G4 :: SizeSlug
+ Network.DO: G48 :: SizeSlug
+ Network.DO: G64 :: SizeSlug
+ Network.DO: G8 :: SizeSlug
+ Network.DO: G96 :: SizeSlug
+ Network.DO: Image :: Id -> String -> String -> Maybe Slug -> Bool -> [Region] -> Bytes Giga -> Date -> ImageType -> Image
+ Network.DO: InProgress :: ActionStatus
+ Network.DO: Key :: Id -> String -> String -> String -> Key
+ Network.DO: M512 :: SizeSlug
+ Network.DO: MX :: DNSType
+ Network.DO: MakeSnapshot :: DropletActionType
+ Network.DO: NS :: DNSType
+ Network.DO: NetworkV4 :: IP -> IP -> IP -> NetType -> Network a
+ Network.DO: NetworkV6 :: IP -> Int -> IP -> NetType -> Network a
+ Network.DO: Networks :: [Network V4] -> [Network V6] -> Networks
+ Network.DO: New :: Status
+ Network.DO: NoNetworks :: Networks
+ Network.DO: NoRegion :: Region
+ Network.DO: Off :: Status
+ Network.DO: PTR :: DNSType
+ Network.DO: PowerOff :: DropletActionType
+ Network.DO: PowerOn :: DropletActionType
+ Network.DO: Private :: NetType
+ Network.DO: Public :: NetType
+ Network.DO: Region :: String -> Slug -> [SizeSlug] -> Bool -> Region
+ Network.DO: RegionSlug :: Slug -> Region
+ Network.DO: SRV :: DNSType
+ Network.DO: Size :: SizeSlug -> Bytes Mega -> Int -> Bytes Giga -> TransferRate -> Price -> Price -> [Region] -> Bool -> Size
+ Network.DO: Snapshot :: ImageType
+ Network.DO: TXT :: DNSType
+ Network.DO: TargetDroplet :: Id -> FloatingIPTarget
+ Network.DO: TargetRegion :: Slug -> FloatingIPTarget
+ Network.DO: Temporary :: ImageType
+ Network.DO: Tool :: Maybe URI -> Maybe AuthToken -> Bool -> ToolConfiguration
+ Network.DO: Unassign :: IPActionType
+ Network.DO: UnassignIP :: IPAction
+ Network.DO: [actionCompletedAt] :: ActionResult result -> Maybe Date
+ Network.DO: [actionId] :: ActionResult result -> Id
+ Network.DO: [actionRegionSlug] :: ActionResult result -> Region
+ Network.DO: [actionResourceId] :: ActionResult result -> Id
+ Network.DO: [actionResourceType] :: ActionResult result -> String
+ Network.DO: [actionStartedAt] :: ActionResult result -> Maybe Date
+ Network.DO: [actionStatus] :: ActionResult result -> ActionStatus
+ Network.DO: [actionType] :: ActionResult result -> result
+ Network.DO: [authToken] :: ToolConfiguration -> Maybe AuthToken
+ Network.DO: [backgroundCreate] :: BoxConfiguration -> Bool
+ Network.DO: [backup_ids] :: Droplet -> [Id]
+ Network.DO: [boxRegion] :: BoxConfiguration -> Region
+ Network.DO: [bytesSize] :: Bytes a -> Int
+ Network.DO: [configImageSlug] :: BoxConfiguration -> ImageSlug
+ Network.DO: [configName] :: BoxConfiguration -> String
+ Network.DO: [created_at] :: Droplet -> Date
+ Network.DO: [disk] :: Droplet -> Bytes Giga
+ Network.DO: [distribution] :: Image -> String
+ Network.DO: [domainName] :: Domain -> DomainName
+ Network.DO: [domainTTL] :: Domain -> Maybe Int
+ Network.DO: [domain] :: DomainName -> String
+ Network.DO: [dropletId] :: Droplet -> Id
+ Network.DO: [floatingDroplet] :: FloatingIP -> Maybe Droplet
+ Network.DO: [floatingIp] :: FloatingIP -> IP
+ Network.DO: [floatingRegion] :: FloatingIP -> Region
+ Network.DO: [gateway] :: Network a -> IP
+ Network.DO: [imageId] :: Image -> Id
+ Network.DO: [imageName] :: Image -> String
+ Network.DO: [imageRegions] :: Image -> [Region]
+ Network.DO: [imageSlug] :: Image -> Maybe Slug
+ Network.DO: [imageType] :: Image -> ImageType
+ Network.DO: [image_created_at] :: Image -> Date
+ Network.DO: [ip_address] :: Network a -> IP
+ Network.DO: [keyFingerprint] :: Key -> String
+ Network.DO: [keyId] :: Key -> Id
+ Network.DO: [keyName] :: Key -> String
+ Network.DO: [keys] :: BoxConfiguration -> [KeyId]
+ Network.DO: [locked] :: Droplet -> Bool
+ Network.DO: [memory] :: Droplet -> Bytes Mega
+ Network.DO: [min_disk_size] :: Image -> Bytes Giga
+ Network.DO: [msg] :: Error -> String
+ Network.DO: [name] :: Droplet -> String
+ Network.DO: [netType] :: Network a -> NetType
+ Network.DO: [netmask] :: Network a -> IP
+ Network.DO: [netmask_v6] :: Network a -> Int
+ Network.DO: [networks] :: Droplet -> Networks
+ Network.DO: [publicImage] :: Image -> Bool
+ Network.DO: [publicKey] :: Key -> String
+ Network.DO: [quiet] :: ToolConfiguration -> Bool
+ Network.DO: [recordData] :: DomainRecord -> String
+ Network.DO: [recordId] :: DomainRecord -> Id
+ Network.DO: [recordName] :: DomainRecord -> String
+ Network.DO: [recordPort] :: DomainRecord -> Maybe Int
+ Network.DO: [recordPriority] :: DomainRecord -> Maybe Int
+ Network.DO: [recordType] :: DomainRecord -> DNSType
+ Network.DO: [recordWeight] :: DomainRecord -> Maybe Int
+ Network.DO: [regionAvailable] :: Region -> Bool
+ Network.DO: [regionName] :: Region -> String
+ Network.DO: [regionSizes] :: Region -> [SizeSlug]
+ Network.DO: [regionSlug] :: Region -> Slug
+ Network.DO: [region] :: Droplet -> Region
+ Network.DO: [size] :: BoxConfiguration -> SizeSlug
+ Network.DO: [size_slug] :: Droplet -> SizeSlug
+ Network.DO: [slackUri] :: ToolConfiguration -> Maybe URI
+ Network.DO: [snapshot_ids] :: Droplet -> [Id]
+ Network.DO: [status] :: Droplet -> Status
+ Network.DO: [szAvailable] :: Size -> Bool
+ Network.DO: [szDisk] :: Size -> Bytes Giga
+ Network.DO: [szMemory] :: Size -> Bytes Mega
+ Network.DO: [szPrice_Hourly] :: Size -> Price
+ Network.DO: [szPrice_Monthly] :: Size -> Price
+ Network.DO: [szRegions] :: Size -> [Region]
+ Network.DO: [szSlug] :: Size -> SizeSlug
+ Network.DO: [szTransfer] :: Size -> TransferRate
+ Network.DO: [szVcpus] :: Size -> Int
+ Network.DO: [theDate] :: Date -> UTCTime
+ Network.DO: [v4] :: Networks -> [Network V4]
+ Network.DO: [v6] :: Networks -> [Network V6]
+ Network.DO: [vcpus] :: Droplet -> Int
+ Network.DO: [zone_file] :: Domain -> Maybe String
+ Network.DO: assignFloatingIP :: (Monad w) => IP -> Id -> Command w (Result (ActionResult IPActionType))
+ Network.DO: createDomain :: (Monad w) => DomainName -> IP -> Command w (Result Domain)
+ Network.DO: createDroplet :: (Monad w) => BoxConfiguration -> Command w (Either Error Droplet)
+ Network.DO: createFloatingIP :: (Monad w) => FloatingIPTarget -> Command w (Result FloatingIP)
+ Network.DO: createRecord :: (Monad w) => DomainName -> DomainRecord -> Command w (Result DomainRecord)
+ Network.DO: data Action
+ Network.DO: data ActionResult result
+ Network.DO: data ActionStatus
+ Network.DO: data BoxConfiguration
+ Network.DO: data DNSType
+ Network.DO: data Domain
+ Network.DO: data DomainConfig
+ Network.DO: data DomainRecord
+ Network.DO: data Droplet
+ Network.DO: data DropletActionType
+ Network.DO: data FloatingIP
+ Network.DO: data FloatingIPTarget
+ Network.DO: data Giga
+ Network.DO: data IPAction
+ Network.DO: data IPActionType
+ Network.DO: data Image
+ Network.DO: data ImageType
+ Network.DO: data Key
+ Network.DO: data Mega
+ Network.DO: data NetType
+ Network.DO: data Network a
+ Network.DO: data Networks
+ Network.DO: data Region
+ Network.DO: data Size
+ Network.DO: data SizeSlug
+ Network.DO: data Status
+ Network.DO: data ToolConfiguration
+ Network.DO: data V4
+ Network.DO: data V6
+ Network.DO: defaultImage :: ImageSlug
+ Network.DO: deleteDomain :: (Monad w) => DomainName -> Command w (Maybe String)
+ Network.DO: deleteFloatingIP :: (Monad w) => IP -> Command w (Maybe String)
+ Network.DO: deleteRecord :: (Monad w) => DomainName -> Id -> Command w (Maybe String)
+ Network.DO: destroyDroplet :: (Monad w) => Integer -> Command w (Maybe String)
+ Network.DO: dropletAction :: (Monad w) => Id -> Action -> Command w (Result (ActionResult DropletActionType))
+ Network.DO: dropletConsole :: (Monad w) => Droplet -> Command w (Result ())
+ Network.DO: error :: String -> Result a
+ Network.DO: failParse :: (Show a1, Monad m) => a1 -> m a
+ Network.DO: findByIdOrName :: String -> [Droplet] -> [Droplet]
+ Network.DO: generateName :: IO String
+ Network.DO: getAction :: (Monad w) => Id -> Id -> Command w (Result (ActionResult DropletActionType))
+ Network.DO: getAuthFromEnv :: IO (Maybe AuthToken)
+ Network.DO: jsonBytes :: Int -> Parser (Bytes a)
+ Network.DO: jsonNetwork :: (FromJSON a3, FromJSON a2, FromJSON a1, FromJSON a) => (a3 -> a2 -> a1 -> a -> b) -> Object -> Parser b
+ Network.DO: listDomains :: (Monad w) => Command w [Domain]
+ Network.DO: listDropletSnapshots :: (Monad w) => Id -> Command w [Image]
+ Network.DO: listDroplets :: (Monad w) => Command w [Droplet]
+ Network.DO: listFloatingIPs :: (Monad w) => Command w [FloatingIP]
+ Network.DO: listImages :: (Monad w) => Command w [Image]
+ Network.DO: listKeys :: (Monad w) => Command w [Key]
+ Network.DO: listRecords :: (Monad w) => DomainName -> Command w [DomainRecord]
+ Network.DO: listRegions :: (Monad w) => Command w [Region]
+ Network.DO: listSizes :: (Monad w) => Command w [Size]
+ Network.DO: newtype Bytes a
+ Network.DO: newtype Date
+ Network.DO: newtype DomainName
+ Network.DO: newtype Error
+ Network.DO: outputResult :: (Pretty a, MonadIO m) => a -> m ()
+ Network.DO: parseRecord :: String -> Result DomainRecord
+ Network.DO: publicIP :: Droplet -> Maybe IP
+ Network.DO: runDO :: Command IO a -> Maybe AuthToken -> IO a
+ Network.DO: runDOEnv :: Command IO a -> IO a
+ Network.DO: showDroplet :: (Monad w) => Integer -> Command w (Either Error Droplet)
+ Network.DO: sizeSlugs :: [String]
+ Network.DO: type AuthToken = String
+ Network.DO: type Command w a = FreeT (DO :+: (DropletCommands :+: (IPCommands :+: DomainCommands))) (RESTT w) a
+ Network.DO: type Id = Integer
+ Network.DO: type ImageSlug = String
+ Network.DO: type KeyId = Int
+ Network.DO: type Price = Double
+ Network.DO: type Result a = Either Error a
+ Network.DO: type Slug = String
+ Network.DO: type TransferRate = Double
+ Network.DO: type URI = String
+ Network.DO: unassignFloatingIP :: (Monad w) => IP -> Command w (Result (ActionResult IPActionType))

Files

README.md view
@@ -2,40 +2,180 @@  [![Build Status](https://travis-ci.org/capital-match/hdo.svg?branch=master)](https://travis-ci.org/capital-match/hdo) -**WARNING** This implementation still covers only a small subset of DO API- This is a [Digital Ocean](https://www.digitalocean.com/) client written in [Haskell](http://haskell.org). It can be used either as a library or as command-line utility. +# Build -## Compile-`stack setup`-`stack build`+*hdo* is built with [stack](https://github.com/commercialhaskell/stack/), using [Stackage LTS-7.7](https://www.stackage.org/lts-7.7) dependencies. To build, follow the standard instructions: -## Usage-First export the AUTH_TOKEN:-`export AUTH_TOKEN=2342342341234eaf`+Install the correct GHC version: -List existing droplets:-`stack exec docean -- droplets list`+> stack setup  -List existing regions:-`stack exec docean -- regions list`+Build and install the package locally, possibly making executable `docean` available from `PATH`: -## Implemented commands:+> stack install++# Usage++## Command-line Tool ++Command-line tool is named `docean`. If it's been installed, it should be available in your PATH. Otherwise, you can run it from the source directory using `stack exec docean -- <args>`. `docean` expects authentication token to be available from environment variable `AUTH_TOKEN`, see [Digital Ocean API](https://developers.digitalocean.com/documentation/v2/#authentication) for details on how to get that authentication token.++> export AUTH_TOKEN=2342342341234eaf++## Available Commands++**NOTE**: Not all the available APIs from DO are implemented (yet).++### Droplets++#### Creating a Droplet++This is the most complex command, as creating a droplet can use a lot of different options.+ ```-droplets create-droplets destroy <dropletId>-droplets list-droplets power_off <dropletId>-droplets power_on  <dropletId>-droplets snapshot  <dropletId> <snapshotName>-droplets action    <dropletId> <actionId>-droplets <dropletId> snapshots-droplets <dropletId>-droplets ssh <dropletId or Name>-images list-regions list-keys list-sizes list+$ docean droplets create -r ams2 -s 1gb -i ubuntu-16-04-x64 -n deploy-test -k '[2436510]'+waiting for droplet deploy-test to become Active: 60s+waiting for droplet deploy-test to become Active: 59s+waiting for droplet deploy-test to become Active: 58s+waiting for droplet deploy-test to become Active: 57s+waiting for droplet deploy-test to become Active: 56s+waiting for droplet deploy-test to become Active: 55s+waiting for droplet deploy-test to become Active: 54s+waiting for droplet deploy-test to become Active: 53s+waiting for droplet deploy-test to become Active: 52s+30876135+     deploy-test [Active] ams2: Amsterdam 2+     1024M/30G/1 cores+     IPv4+       95.85.44.239/255.255.255.0 [Public]+     IPv6 ```++The available parameters are:++* `-r|--region`: Sets the region where droplet is created, using the *region slug*+* `-s|--size`: Sets the size of the droplet, from `512mb` to `96gb`+* `-n|--name`: Name of the newly created droplet+* `-i|--image`: Slug of the image to use as base image for droplet+* `-k|--keys`: List (in the form readable by Haskell to convert to type `[Int]`) of keys to add for accessing the droplet+* `-b|--background`: Create droplet in the background, returning only the identifier of action to be checked later. If not set, creation is *synchronous*: The client will check wait for droplet to be up for 60s.++When successfully created, the main characteristics of the droplet are printed to the console.++#### Other Droplet Operations++Destroy an existing droplet:++```+$ droplets destroy 30876135+-+```++List all created droplets:++```+$ docean droplets list+21002433+     droplet-01 [Active] sgp1: Singapore 1+     1024M/30G/1 cores+     IPv4+       1.2.3.4/255.255.240.0 [Public]+     IPv6+26082279+     droplet-02 [Active] sgp1: Singapore 1+     512M/20G/1 cores+     IPv4+       1.2.3.5/255.255.240.0 [Public]+     IPv6+31114780+     droplet-03 [Active] ams2: Amsterdam 2+     2048M/40G/2 cores+     IPv4+       5.6.7.8/255.255.240.0 [Public]+     IPv6+```++Shutdown/startup a droplet:++```+$ docean droplets power_off 31166171+[163908062] 2016-11-01 21:08:46 UTC -> -+     31166171 PowerOff: InProgress+```++```+$ docean droplets power_on 31166171+[163908350] 2016-11-01 21:10:16 UTC -> -+     31166171 PowerOn: InProgress+```++Retrieve status of an action initiated on the droplet (usually related to power on/off or snapshots):++```+$ docean droplets action 31166171 163908062+[163908062] 2016-11-01 21:08:46 UTC -> 2016-11-01 21:08:58 UTC+     31166171 PowerOff: Completed+```++Take a snapshot of an existing droplet (must be powered off):++```+$ docean droplets snapshot 31166171 mysnapshot+[163908530] 2016-11-01 21:11:19 UTC -> -+     31166171 MakeSnapshot: InProgress+```++List existing snapshots for droplet:++```+$ docean droplets 31166171 snapshots+20648640 mysnapshot Ubuntu+```++Connect to the droplet through SSH (can be given a name or a droplet ID):++```+$ docean droplets ssh 31166171+The authenticity of host '1.2.3.4 (1.2.3.4)' can't be established.+RSA key fingerprint is 3f:16:4f:f4:25:85:85:77:1e:90:bd:8c:6b:90:48:eb.+Are you sure you want to continue connecting (yes/no)? yes+Warning: Permanently added '1.2.3.4' (RSA) to the list of known hosts.+Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-45-generic x86_64)++ * Documentation:  https://help.ubuntu.com+ * Management:     https://landscape.canonical.com+ * Support:        https://ubuntu.com/advantage++0 packages can be updated.+0 updates are security updates.++++The programs included with the Ubuntu system are free software;+the exact distribution terms for each program are described in the+individual files in /usr/share/doc/*/copyright.++Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by+applicable law.++root@droplet:~# +```++### Images++### Regions++### Keys++### Sizes++### Floating IPs++### Domains++## Library++TBD
hdo.cabal view
@@ -1,5 +1,5 @@ Name: hdo-Version: 0.2+Version: 0.3 Cabal-Version: >= 1.18 Maintainer: Arnaud Bailly <arnaud.oqube@gmail.com> Author: Arnaud Bailly@@ -20,24 +20,30 @@   Default-Language:   Haskell2010   GHC-Options:  -Wall -fno-warn-orphans   Hs-Source-Dirs:  src-  Other-Modules:  Network.REST.Commands, Network.REST.Wreq, Network.SSH-  exposed-modules: Network.DO.Commands-                 , Network.DO.Types+  Other-Modules:   Network.REST.Commands, Network.REST.Conduit, Network.SSH, Network.DO.Types                  , Network.DO.Net, Network.DO.Net.Common+                 , Network.DO.Commands                  , Network.DO.Droplets.Commands, Network.DO.Droplets.Net, Network.DO.Droplets.Utils+                 , Network.DO.IP, Network.DO.IP.Commands, Network.DO.IP.Net+                 , Network.DO.Domain, Network.DO.Domain.Commands, Network.DO.Domain.Net                  , Network.DO.Names, Network.DO.Pairing                  , Network.REST, Network.DO.Pretty+                 +  exposed-modules: Network.DO   Build-Depends: aeson                , base >= 4 && < 5                , bytestring+               , case-insensitive                , comonad                , data-default                , free+               , http-conduit                , iproute                , lens                , pretty                , mtl                , network-uri+               , parsec                , random                , text                , tagged@@ -46,7 +52,6 @@                , unix, process                , unordered-containers                , vector-               , wreq  Executable docean   Default-Language:   Haskell2010
main/hdo.hs view
@@ -1,30 +1,16 @@-{-# LANGUAGE DoAndIfThenElse       #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE RecordWildCards       #-}-{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE DoAndIfThenElse, MultiParamTypeClasses, OverloadedStrings, RecordWildCards, ScopedTypeVariables #-} module Main where -import           Control.Exception            (catch, throw)-import           Control.Monad.IO.Class       (MonadIO (..))-import           Control.Monad.Trans.Free     (FreeT)-import           Data.Functor.Sum+import           Control.Exception      (catch, throw)+import           Control.Monad.IO.Class (MonadIO (..)) import           Data.Maybe-import           Data.Monoid                  ((<>))-import           Prelude                      as P hiding (error)+import           Data.Monoid            ((<>))+import           Network.DO+import           Prelude                as P hiding (error) import           System.Console.GetOpt import           System.Environment import           System.IO-import           System.IO.Error              (isDoesNotExistError)-import           Network.DO.Commands-import           Network.DO.Droplets.Commands-import           Network.DO.Droplets.Utils-import           Network.DO.Names-import           Network.DO.Net-import           Network.DO.Pairing-import           Network.DO.Pretty            (outputResult)-import           Network.DO.Types             as DO-import           Network.REST+import           System.IO.Error        (isDoesNotExistError)  generalOptions :: [OptDescr (ToolConfiguration -> ToolConfiguration)] generalOptions = [ Option ['t'] ["auth-token"]@@ -56,16 +42,13 @@                          "add a key to access box (default: '[]')"                        ] -getAuthFromEnv :: IO (Maybe AuthToken)-getAuthFromEnv = (Just `fmap` getEnv "AUTH_TOKEN") `catch` (\ (e :: IOError) -> if isDoesNotExistError e then return Nothing else throw e)- getSlackUriFromEnv :: IO (Maybe URI) getSlackUriFromEnv = (Just `fmap` getEnv "SLACK_URI") `catch` (\ (e :: IOError) -> if isDoesNotExistError e then return Nothing else throw e)  defaultBox :: IO BoxConfiguration defaultBox = do   name <- generateName-  return $ BoxConfiguration name (RegionSlug "ams2") G4 defaultImage [429079] False+  return $ BoxConfiguration name (RegionSlug "ams2") G4 defaultImage [] False  defaultTool :: IO ToolConfiguration defaultTool = do@@ -89,38 +72,56 @@ main = do   hSetBuffering stdin NoBuffering   args <- getArgs-  (opts, cmds) <- parseOptions args-  runWreq $ pairEffectM (\ _ b -> return b) (mkDOClient opts) (parseCommandOptions cmds)+  (_, cmds) <- parseOptions args+  runDOEnv (parseCommandOptions cmds) -parseCommandOptions :: (MonadIO m) => [String] -> FreeT (Sum DO DropletCommands) (RESTT m) ()+parseCommandOptions :: (MonadIO m) => [String] -> Command m () parseCommandOptions ("droplets":"create":args) = do   b <- liftIO defaultBox   case getOpt Permute createDropletOptions args of-   (c,[],[])  -> injr (createDroplet (foldl (flip P.id) b c)) >>= outputResult+   (c,[],[])  -> createDroplet (foldl (flip P.id) b c) >>= outputResult    (_,_,errs) -> liftIO $ ioError (userError (concat errs  ++ usage))-parseCommandOptions ("droplets":"destroy":dropletId:[]) = injr ( destroyDroplet (P.read dropletId) ) >>= outputResult-parseCommandOptions ("droplets":"list":_)               = injr ( listDroplets ) >>= outputResult+parseCommandOptions ("droplets":"destroy":dropletId:[]) = destroyDroplet (P.read dropletId) >>= outputResult+parseCommandOptions ("droplets":"list":_)               = listDroplets >>= outputResult parseCommandOptions ("droplets":"power_off":dropletId:[])-                                                         = injr ( dropletAction (P.read dropletId) DoPowerOff) >>= outputResult+                                                         = dropletAction (P.read dropletId) DoPowerOff >>= outputResult parseCommandOptions ("droplets":"power_on":dropletId:[])-                                                         = injr ( dropletAction (P.read dropletId) DoPowerOn) >>= outputResult+                                                         = dropletAction (P.read dropletId) DoPowerOn >>= outputResult parseCommandOptions ("droplets":"snapshot":dropletId:snapshotName:[])-                                                         = injr ( dropletAction (P.read dropletId) (CreateSnapshot snapshotName)) >>= outputResult+                                                         = dropletAction (P.read dropletId) (CreateSnapshot snapshotName) >>= outputResult parseCommandOptions ("droplets":"action":dropletId:actionId:[])-                                                         = injr ( getAction (P.read dropletId)  (P.read actionId)) >>= outputResult+                                                         = getAction (P.read dropletId)  (P.read actionId) >>= outputResult parseCommandOptions ("droplets":dropletId:"snapshots":[])-                                                         = injr ( listDropletSnapshots (P.read dropletId)) >>= outputResult+                                                         = listDropletSnapshots (P.read dropletId) >>= outputResult parseCommandOptions ("droplets":dropletId:[])-                                                         = injr ( showDroplet (P.read dropletId)) >>= outputResult+                                                         = showDroplet (P.read dropletId) >>= outputResult parseCommandOptions ("droplets":"ssh":dropletIdOrName:[])                                                          =  (do-                                                                droplets <- injr $ (findByIdOrName dropletIdOrName) <$> listDroplets+                                                                droplets <- findByIdOrName dropletIdOrName <$> listDroplets                                                                 case droplets of-                                                                 (did:_) -> injr $ dropletConsole did+                                                                 (did:_) -> dropletConsole did                                                                  []      -> return (error $ "no droplet with id or name " <> dropletIdOrName)                                                             ) >>= outputResult-parseCommandOptions ("images":"list":_)                  = injl listImages >>= outputResult-parseCommandOptions ("regions":"list":_)                 = injl listRegions >>= outputResult-parseCommandOptions ("keys":"list":_)                    = injl listKeys >>= outputResult-parseCommandOptions ("sizes":"list":_)                   = injl listSizes >>= outputResult-parseCommandOptions e                                    = fail $ "I don't know how to interpret commands " ++ unwords e ++ "\n" ++ usage+parseCommandOptions ("images":"list":_)                  = listImages >>= outputResult+parseCommandOptions ("regions":"list":_)                 = listRegions >>= outputResult+parseCommandOptions ("keys":"list":_)                    = listKeys >>= outputResult+parseCommandOptions ("sizes":"list":_)                   = listSizes >>= outputResult+parseCommandOptions ("ips":"list":_)                     = listFloatingIPs >>= outputResult+parseCommandOptions ("ips":"create":dropletOrRegion:[])  = do+  regions <- listRegions+  outputResult =<< if dropletOrRegion `elem` map regionSlug regions+    then createFloatingIP (TargetRegion dropletOrRegion)+    else createFloatingIP (TargetDroplet $ read dropletOrRegion)+parseCommandOptions ("ips":"delete":ip:[])     = deleteFloatingIP (P.read ip) >>= outputResult+parseCommandOptions ("ips":ip:"assign":did:[]) = assignFloatingIP (P.read ip) (P.read did) >>= outputResult+parseCommandOptions ("ips":ip:"unassign": [])  = unassignFloatingIP (P.read ip) >>= outputResult+parseCommandOptions ("dns":"list":_)             = listDomains >>= outputResult+parseCommandOptions ("dns":"create":name:ip:[])  = createDomain (P.read name) (P.read ip) >>= outputResult+parseCommandOptions ("dns":"delete":name:[])     = deleteDomain (P.read name) >>= outputResult+parseCommandOptions ("dns":name:"list":_)        = listRecords (P.read name) >>= outputResult+parseCommandOptions ("dns":name:"create":rest)   =+  case (parseRecord $ unwords rest) of+    Left (Error e) -> fail e+    Right r        -> createRecord (P.read name) r >>= outputResult+parseCommandOptions ("dns":name:"delete":rid:[]) = deleteRecord (P.read name) (P.read rid) >>= outputResult+parseCommandOptions e                          = fail $ "I don't know how to interpret commands " ++ unwords e ++ "\n" ++ usage
+ src/Network/DO.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE ScopedTypeVariables, TypeOperators #-}+module Network.DO(+  -- * Types+  Command,+  module Network.DO.Types,+  -- * Generic Commands+  listKeys, listSizes, listRegions, listImages,+  -- * Droplets Commands+  listDroplets, createDroplet, showDroplet, destroyDroplet,+  dropletAction, dropletConsole, getAction, listDropletSnapshots,+  -- * Floating IPs Commands+  listFloatingIPs, createFloatingIP, deleteFloatingIP, assignFloatingIP, unassignFloatingIP,+  -- * Domains Commands+  listDomains, createDomain, deleteDomain,+  listRecords, createRecord, deleteRecord,+  -- * Utilities+  runDOEnv, runDO, getAuthFromEnv, outputResult,+  generateName,+  module Network.DO.Droplets.Utils) where++import           Control.Exception            (catch, throw)+import           Control.Monad.Trans.Free+import           Data.IP+import qualified Network.DO.Commands          as C+import qualified Network.DO.Domain            as C+import qualified Network.DO.Droplets.Commands as C+import           Network.DO.Droplets.Utils+import qualified Network.DO.IP.Commands       as C+import           Network.DO.Names+import           Network.DO.Net+import           Network.DO.Pairing+import           Network.DO.Pretty+import           Network.DO.Types+import           Network.REST+import           System.Environment           (getEnv)+import           System.IO.Error              (isDoesNotExistError)++type Command w a = FreeT (C.DO :+: C.DropletCommands :+: C.IPCommands :+: C.DomainCommands) (RESTT w) a++listKeys :: (Monad w) => Command w [Key]+listKeys = injl C.listKeys++listSizes :: (Monad w) => Command w [Size]+listSizes = injl C.listSizes++listImages  :: (Monad w) => Command w  [Image]+listImages = injl C.listImages++listRegions :: (Monad w) => Command w [Region]+listRegions = injl C.listRegions++listFloatingIPs :: (Monad w) => Command w [FloatingIP]+listFloatingIPs = injrrl C.listFloatingIPs++createFloatingIP :: (Monad w) => FloatingIPTarget -> Command w (Result FloatingIP)+createFloatingIP = injrrl . C.createFloatingIP++deleteFloatingIP :: (Monad w) => IP -> Command w (Maybe String)+deleteFloatingIP = injrrl . C.deleteFloatingIP++assignFloatingIP :: (Monad w) => IP -> Id -> Command w (Result (ActionResult IPActionType))+assignFloatingIP ip did = injrrl $ C.floatingIPAction ip (AssignIP did)++unassignFloatingIP :: (Monad w) => IP -> Command w (Result (ActionResult IPActionType))+unassignFloatingIP ip = injrrl $ C.floatingIPAction ip UnassignIP++listDomains :: (Monad w) => Command w [Domain]+listDomains = injrrr C.listDomains++createDomain :: (Monad w) => DomainName -> IP -> Command w (Result Domain)+createDomain dname ip = injrrr $ C.createDomain dname ip++deleteDomain :: (Monad w) => DomainName -> Command w (Maybe String)+deleteDomain = injrrr . C.deleteDomain++listRecords :: (Monad w) => DomainName -> Command w [DomainRecord]+listRecords = injrrr . C.listRecords++createRecord :: (Monad w) => DomainName -> DomainRecord -> Command w (Result DomainRecord)+createRecord dname ip = injrrr $ C.createRecord dname ip++deleteRecord :: (Monad w) => DomainName -> Id -> Command w (Maybe String)+deleteRecord dname rid = injrrr $ C.deleteRecord dname rid++listDroplets :: (Monad w) => Command w [Droplet]+listDroplets = injrl C.listDroplets++createDroplet :: (Monad w) => BoxConfiguration -> Command w (Either Error Droplet)+createDroplet = injrl . C.createDroplet++showDroplet :: (Monad w) => Integer -> Command w (Either Error Droplet)+showDroplet = injrl . C.showDroplet++destroyDroplet :: (Monad w) => Integer -> Command w (Maybe String)+destroyDroplet = injrl . C.destroyDroplet++dropletAction :: (Monad w) => Id -> Action -> Command w (Result (ActionResult DropletActionType))+dropletAction did = injrl . C.dropletAction did++dropletConsole :: (Monad w) => Droplet -> Command w (Result ())+dropletConsole = injrl . C.dropletConsole++getAction :: (Monad w) => Id -> Id -> Command w (Result (ActionResult DropletActionType))+getAction  did = injrl . C.getAction did++listDropletSnapshots :: (Monad w) => Id -> Command w [Image]+listDropletSnapshots = injrl . C.listDropletSnapshots++-- | Run DO actions, extracting authentication token from environment variable `AUTH_TOKEN`.+runDOEnv :: Command IO a -> IO a+runDOEnv actions = getAuthFromEnv >>= runDO actions++-- | Run DO actions, passing a built authentication token.+runDO :: Command IO a -> Maybe AuthToken -> IO a+runDO actions token =  runConduit $ pairEffectM (\ _ b -> return b) (mkDOClient $ Tool Nothing token False) actions+++getAuthFromEnv :: IO (Maybe AuthToken)+getAuthFromEnv = (Just `fmap` getEnv "AUTH_TOKEN") `catch` (\ (e :: IOError) -> if isDoesNotExistError e then return Nothing else throw e)
src/Network/DO/Commands.hs view
@@ -33,9 +33,9 @@ listRegions = ListRegions P.id  -- dual type, for creating interpreters-data CoDO m k = CoDO { listKeysH   :: (m [Key], k)-                     , listSizesH  :: (m [Size], k)-                     , listImagesH :: (m [Image], k)+data CoDO m k = CoDO { listKeysH    :: (m [Key], k)+                     , listSizesH   :: (m [Size], k)+                     , listImagesH  :: (m [Image], k)                      , listRegionsH :: (m [Region], k)                      } deriving Functor @@ -44,7 +44,7 @@  -- pair DSL with interpreter within some monadic context instance (Monad m) => PairingM (CoDO m) DO m where-  pairM f (CoDO ks _  _ _)  (ListKeys k)   = pairM f ks k-  pairM f (CoDO _ szs _ _)  (ListSizes k)  = pairM f szs k-  pairM f (CoDO _ _ imgs _)  (ListImages k) = pairM f imgs k-  pairM f (CoDO _ _ _ rgns)  (ListRegions k) = pairM f rgns k+  pairM f (CoDO ks _  _ _ )  (ListKeys k)   = pairM f ks k+  pairM f (CoDO _ szs _ _ )  (ListSizes k)  = pairM f szs k+  pairM f (CoDO _ _ imgs _ )  (ListImages k) = pairM f imgs k+  pairM f (CoDO _ _ _ rgns )  (ListRegions k) = pairM f rgns k
+ src/Network/DO/Domain.hs view
@@ -0,0 +1,4 @@+module Network.DO.Domain(module C) where++import           Network.DO.Domain.Commands as C+import           Network.DO.Domain.Net      as C
+ src/Network/DO/Domain/Commands.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Network.DO.Domain.Commands where++import           Control.Comonad.Trans.Cofree+import           Control.Monad.Trans.Free+import           Data.IP+import           Network.DO.Pairing+import           Network.DO.Types+import           Prelude                      as P++data DomainCommands a = ListDomains ([Domain] -> a)+                      | CreateDomain DomainName IP (Result Domain -> a)+                      | DeleteDomain DomainName (Maybe String -> a)+                      | ListRecords DomainName ([DomainRecord] -> a)+                      | CreateRecord DomainName DomainRecord (Result DomainRecord -> a)+                      | DeleteRecord DomainName Id (Maybe String -> a)+                      deriving (Functor)++type DomainCommandsT = FreeT DomainCommands++-- smart constructors+listDomains :: DomainCommands [Domain]+listDomains = ListDomains P.id++createDomain :: DomainName -> IP -> DomainCommands (Result Domain)+createDomain dname ip = CreateDomain dname ip P.id++deleteDomain :: DomainName -> DomainCommands (Maybe String)+deleteDomain ip = DeleteDomain ip P.id++listRecords :: DomainName -> DomainCommands [DomainRecord]+listRecords n = ListRecords n P.id++createRecord :: DomainName -> DomainRecord -> DomainCommands (Result DomainRecord)+createRecord dname record = CreateRecord dname record P.id++deleteRecord :: DomainName -> Id -> DomainCommands (Maybe String)+deleteRecord dname rid = DeleteRecord dname rid P.id++-- dual type, for creating interpreters+data CoDomainCommands m k =+  CoDomainCommands { listDomainsH  :: (m [Domain], k)+                   , createDomainH :: DomainName -> IP -> (m (Result Domain), k)+                   , deleteDomainH :: DomainName -> (m (Maybe String), k)+                   , listRecordsH  :: DomainName -> (m [DomainRecord], k)+                   , createRecordH :: DomainName -> DomainRecord -> (m (Result DomainRecord), k)+                   , deleteRecordH :: DomainName -> Id -> (m (Maybe String), k)+                   } deriving Functor++-- Cofree closure of CoDomainCommands functor+type CoDomainCommandsT m = CofreeT (CoDomainCommands m)++-- pair DSL with interpreter within some monadic context+instance (Monad m) => PairingM (CoDomainCommands m) DomainCommands m where+  pairM f (CoDomainCommands ks _ _ _ _ _)    (ListDomains k)      = pairM f ks k+  pairM f (CoDomainCommands _  tgt _ _ _ _)  (CreateDomain n i k) = pairM f (tgt n i) k+  pairM f (CoDomainCommands _  _  del _ _ _) (DeleteDomain n k)   = pairM f (del n) k+  pairM f (CoDomainCommands _ _ _ ks _ _)    (ListRecords n k)    = pairM f (ks n) k+  pairM f (CoDomainCommands _  _ _ _ tgt _)  (CreateRecord n r k) = pairM f (tgt n r) k+  pairM f (CoDomainCommands _  _  _ _ _ del) (DeleteRecord n i k) = pairM f (del n i) k
+ src/Network/DO/Domain/Net.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE DoAndIfThenElse       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ScopedTypeVariables   #-}++-- | Network interpreter for Floating Domains specific API+module Network.DO.Domain.Net(dnsCommandsInterpreter) where++import           Control.Applicative+import           Control.Comonad.Env.Class  (ComonadEnv, ask)+import           Control.Monad.Trans        (MonadIO)+import           Data.Aeson                 as A hiding (Result)+import           Data.IP+import           Data.Maybe+import           Data.Proxy+import           Network.DO.Domain.Commands+import           Network.DO.Net.Common+import           Network.DO.Types           as DO hiding (URI)+import           Network.REST+import           Prelude                    as P hiding (error)++domainsURI :: String+domainsURI = "domains"++domainsEndpoint :: String+domainsEndpoint = rootURI </> apiVersion </> domainsURI++instance Listable Domain where+  listEndpoint _ = domainsEndpoint+  listField _    = "domains"++doCreateDomain :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> DomainName -> IP -> (RESTT m (Result Domain), w a)+doCreateDomain w name ip = maybe (return $ error "no authentication token defined", w)+  runQuery+  (authToken (ask w))+  where+    runQuery t = let opts   = authorisation t+                     domain = postJSONWith opts (toURI domainsEndpoint) (toJSON config) >>= return . fromResponse "domain"+                 in (domain, w)++      where+        config = DomainConfig name ip++doDeleteDomain :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> DomainName -> (RESTT m (Maybe String), w a)+doDeleteDomain w name = maybe (return $ Just "no authentication token defined", w)+                  (\ t -> let r = deleteJSONWith (authorisation t) (toURI $ domainsEndpoint </> show name) >> return Nothing+                          in (r, w))+                  (authToken (ask w))++doListRecords :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> DomainName -> (RESTT m [DomainRecord], w a)+doListRecords w name = maybe (return [], w)+                       (\ t -> let records = toList "domain_records" <$> getJSONWith (authorisation t) (toURI $ domainsEndpoint </> show name </> "records")+                         in (records, w))+                       (authToken (ask w))++doCreateRecord :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> DomainName -> DomainRecord -> (RESTT m (Result DomainRecord), w a)+doCreateRecord w name record =+  maybe (return $ error "no authentication token defined", w)+  runQuery+  (authToken (ask w))+  where+    runQuery t = let opts   = authorisation t+                     domain = postJSONWith opts (toURI $ domainsEndpoint </> show name </> "records") (toJSON record) >>= return . fromResponse "domain_record"+                 in (domain, w)++doDeleteRecord :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> DomainName -> Id -> (RESTT m (Maybe String), w a)+doDeleteRecord w name rid =+  maybe (return $ Just "no authentication token defined", w)+  (\ t -> let r = deleteJSONWith (authorisation t) (toURI $ domainsEndpoint </> show name </> "records" </> show rid ) >> return Nothing+          in (r, w))+  (authToken (ask w))++dnsCommandsInterpreter :: (MonadIO m, ComonadEnv ToolConfiguration w) => w a -> CoDomainCommands (RESTT m) (w a)+dnsCommandsInterpreter = CoDomainCommands+                        <$> queryList (Proxy :: Proxy Domain)+                        <*> doCreateDomain+                        <*> doDeleteDomain+                        <*> doListRecords+                        <*> doCreateRecord+                        <*> doDeleteRecord+
src/Network/DO/Droplets/Commands.hs view
@@ -18,8 +18,8 @@ data DropletCommands a = ListDroplets ([Droplet] -> a)                        | CreateDroplet BoxConfiguration (Result Droplet -> a)                        | DestroyDroplet Id (Maybe String -> a)-                       | DropletAction Id Action (Result ActionResult -> a)-                       | GetAction Id Id (Result ActionResult -> a)+                       | DropletAction Id Action (Result (ActionResult DropletActionType) -> a)+                       | GetAction Id Id (Result (ActionResult DropletActionType) -> a)                        | ListSnapshots Id ([Image] -> a)                        | Console Droplet (Result () -> a)                        | ShowDroplet Id (Result Droplet -> a)@@ -41,13 +41,13 @@ destroyDroplet :: Id -> DropletCommands (Maybe String) destroyDroplet did = DestroyDroplet did P.id -dropletAction :: Id -> Action -> DropletCommands (Result ActionResult)+dropletAction :: Id -> Action -> DropletCommands (Result (ActionResult DropletActionType)) dropletAction did action = DropletAction did action P.id  dropletConsole :: Droplet -> DropletCommands (Result ()) dropletConsole droplet = Console droplet P.id -getAction :: Id -> Id -> DropletCommands (Result ActionResult)+getAction :: Id -> Id -> DropletCommands (Result (ActionResult DropletActionType)) getAction did actId = GetAction did actId P.id  listDropletSnapshots :: Id -> DropletCommands [Image]@@ -58,8 +58,8 @@ data CoDropletCommands m k = CoDropletCommands { listDropletsH   :: (m [Droplet], k)                                                , createDropletH  :: BoxConfiguration -> (m (Result Droplet), k)                                                , destroyDropletH :: Id -> (m (Maybe String), k)-                                               , actionDropletH  :: Id -> Action -> (m (Result ActionResult), k)-                                               , getActionH      :: Id -> Id -> (m (Result ActionResult), k)+                                               , actionDropletH  :: Id -> Action -> (m (Result (ActionResult DropletActionType)), k)+                                               , getActionH      :: Id -> Id -> (m (Result (ActionResult DropletActionType)), k)                                                , listSnapshotsH  :: Id -> (m [Image], k)                                                , consoleH        :: Droplet -> (m (Result ()), k)                                                , showDropletH    :: Id -> (m (Result Droplet), k)
src/Network/DO/Droplets/Net.hs view
@@ -1,9 +1,4 @@-{-# LANGUAGE DoAndIfThenElse       #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE RecordWildCards       #-}-{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE DoAndIfThenElse, FlexibleContexts, MultiParamTypeClasses, OverloadedStrings, RecordWildCards, ScopedTypeVariables #-}  -- | Network interpreter for Droplets specific API module Network.DO.Droplets.Net(dropletCommandsInterpreter) where@@ -13,8 +8,6 @@ import           Control.Exception            (IOException) import           Control.Monad.Trans          (MonadIO) import           Data.Aeson                   as A hiding (Result)-import qualified Data.Aeson.Types             as A-import qualified Data.HashMap.Strict          as H import           Data.Maybe import           Data.Monoid                  ((<>)) import           Data.Proxy@@ -23,7 +16,6 @@ import           Network.DO.Net.Common import           Network.DO.Types             as DO hiding (URI) import           Network.REST-import           Network.Wreq                 hiding (Proxy) import           Prelude                      as P hiding (error)  dropletsURI :: String@@ -43,10 +35,6 @@           in (snapshots, w))   (authToken (ask w)) -dropletFromResponse :: Either String Value -> Result Droplet-dropletFromResponse (Right (Object b)) = either error (Right . id) $ A.parseEither parseJSON (b H.! "droplet")-dropletFromResponse v                  = error $ "cannot decode JSON value to a droplet " ++ show v- doCreate :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> BoxConfiguration -> (RESTT m (Result Droplet), w a) doCreate w config = maybe (return $ error "no authentication token defined", w)   runQuery@@ -54,7 +42,7 @@   where     runQuery t = let opts             = authorisation t                      droplets         = postJSONWith opts (toURI dropletsEndpoint) (toJSON config) >>= handleResponse-                     handleResponse d = case dropletFromResponse d of+                     handleResponse d = case fromResponse "droplet" d of                        Right b  -> if (not $ backgroundCreate config)                                   then waitForBoxToBeUp opts 60 b                                   else return (Right b)@@ -67,27 +55,23 @@                                        in (r, w))                                (authToken (ask w)) -actionResult :: Either String Value -> Result ActionResult-actionResult (Right (Object r)) = either error (Right . id) $ A.parseEither parseJSON (r H.! "action")-actionResult e                  = error $ "cannot extract action result from " ++ show e--doAction :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> Id -> Action -> (RESTT m (Result ActionResult), w a)+doAction :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> Id -> Action -> (RESTT m (Result (ActionResult DropletActionType)), w a) doAction w dropletId action = maybe (return $ error "no authentication token defined", w)                               (\ t -> let r = postJSONWith (authorisation t) (toURI $ dropletsEndpoint </> show dropletId </> "actions") (toJSON action)-                                              >>= return . actionResult+                                              >>= return . fromResponse "action"                                       in (r, w))                               (authToken (ask w)) -doGetAction :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> Id -> Id -> (RESTT m (Result ActionResult), w a)+doGetAction :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> Id -> Id -> (RESTT m (Result (ActionResult DropletActionType)), w a) doGetAction w dropletId actionId = maybe (return $ error "no authentication token defined", w)                                    (\ t -> let r = getJSONWith (authorisation t) (toURI $ dropletsEndpoint </> show dropletId </> "actions" </> show actionId)-                                                   >>= return . actionResult . Right+                                                   >>= return . fromResponse "action" . Right                                            in (r, w))                                    (authToken (ask w))  doShowDroplet  :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> Id -> (RESTT m (Result Droplet), w a) doShowDroplet w dropletId = maybe (return $ error "no authentication token defined", w)-                            (\ t -> let r = dropletFromResponse . Right <$> getJSONWith (authorisation t) (toURI $ dropletsEndpoint </> show dropletId)+                            (\ t -> let r = fromResponse "droplet" . Right <$> getJSONWith (authorisation t) (toURI $ dropletsEndpoint </> show dropletId)                                     in (r, w))                             (authToken (ask w)) @@ -96,8 +80,8 @@                                      (\ip -> do                                          s <- ssh ["root@" <> show ip ]                                          case s of-                                          Left (e :: IOException)  -> return $ error (show e)-                                          Right () -> return $ Right ()+                                          Left (e :: IOException) -> return $ error (show e)+                                          Right ()                -> return $ Right ()                                      )                                      (publicIP droplet)                              in (r, w)@@ -107,7 +91,7 @@ waitForBoxToBeUp opts n box  = do   waitFor 1000000 ("waiting for droplet " ++ name box ++ " to become Active: " ++ show (n) ++ "s")   b <- getJSONWith opts (toURI $ dropletsEndpoint </> show (dropletId box))-  case dropletFromResponse (Right b) of+  case fromResponse "droplet" (Right b) of    Right box'-> if status box' == Active                 then return (Right box')                 else waitForBoxToBeUp opts (n-1) box'
+ src/Network/DO/IP.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Network.DO.IP(module Network.DO.IP.Commands+                    ,module Network.DO.IP.Net+                    ) where++import           Network.DO.IP.Commands+import           Network.DO.IP.Net
+ src/Network/DO/IP/Commands.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Network.DO.IP.Commands where++import           Control.Comonad.Trans.Cofree+import           Control.Monad.Trans.Free+import           Data.IP+import           Network.DO.Pairing+import           Network.DO.Types+import           Prelude                      as P++-- functor for DO DSL+data IPCommands a = ListFloatingIPs ([FloatingIP] -> a)+                  | CreateIP FloatingIPTarget (Result FloatingIP -> a)+                  | DeleteIP IP (Maybe String -> a)+                  | ActionIP IP IPAction (Result (ActionResult IPActionType) -> a)+          deriving (Functor)++-- free transformer to embed effects+type IPCommandsT = FreeT IPCommands++-- smart constructors+listFloatingIPs :: IPCommands [FloatingIP]+listFloatingIPs = ListFloatingIPs P.id++createFloatingIP :: FloatingIPTarget -> IPCommands (Result FloatingIP)+createFloatingIP target = CreateIP target P.id++deleteFloatingIP :: IP -> IPCommands (Maybe String)+deleteFloatingIP ip = DeleteIP ip P.id++floatingIPAction :: IP -> IPAction -> IPCommands (Result (ActionResult IPActionType))+floatingIPAction ip action = ActionIP ip action P.id++-- dual type, for creating interpreters+data CoIPCommands m k =+  CoIPCommands { listFloatingIPsH  :: (m [FloatingIP], k)+               , createFloatingIPH :: FloatingIPTarget -> (m (Result FloatingIP), k)+               , deleteIPH         :: IP -> (m (Maybe String), k)+               , actionIPH         :: IP -> IPAction -> (m (Result (ActionResult IPActionType)), k)+               } deriving Functor++-- Cofree closure of CoIPCommands functor+type CoIPCommandsT m = CofreeT (CoIPCommands m)++-- pair DSL with interpreter within some monadic context+instance (Monad m) => PairingM (CoIPCommands m) IPCommands m where+  pairM f (CoIPCommands ks _ _ _)    (ListFloatingIPs k)   = pairM f ks k+  pairM f (CoIPCommands _  tgt _ _)  (CreateIP i k)        = pairM f (tgt i) k+  pairM f (CoIPCommands _  _  del _) (DeleteIP i k)        = pairM f (del i) k+  pairM f (CoIPCommands _  _  _ act) (ActionIP i a k)      = pairM f (act i a) k
+ src/Network/DO/IP/Net.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE DoAndIfThenElse       #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ScopedTypeVariables   #-}++-- | Network interpreter for Floating IPs specific API+module Network.DO.IP.Net(ipCommandsInterpreter) where++import           Control.Applicative+import           Control.Comonad.Env.Class (ComonadEnv, ask)+import           Control.Monad.Trans       (MonadIO)+import           Data.Aeson                as A hiding (Result)+import           Data.IP+import           Data.Maybe+import           Data.Proxy+import           Network.DO.IP.Commands+import           Network.DO.Net.Common+import           Network.DO.Types          as DO hiding (URI)+import           Network.REST+import           Prelude                   as P hiding (error)++floatingIpsURI :: String+floatingIpsURI = "floating_ips"++floatingIpsEndpoint :: String+floatingIpsEndpoint = rootURI </> apiVersion </> floatingIpsURI++instance Listable FloatingIP where+  listEndpoint _ = floatingIpsEndpoint+  listField _    = "floating_ips"++doCreateIP :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> FloatingIPTarget -> (RESTT m (Result FloatingIP), w a)+doCreateIP w config = maybe (return $ error "no authentication token defined", w)+  runQuery+  (authToken (ask w))+  where+    runQuery t = let opts             = authorisation t+                     ip               = postJSONWith opts (toURI floatingIpsEndpoint) (toJSON config) >>= return . fromResponse "floating_ip"+                 in (ip, w)++doDeleteIP :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> IP -> (RESTT m (Maybe String), w a)+doDeleteIP w ip = maybe (return $ Just "no authentication token defined", w)+                  (\ t -> let r = deleteJSONWith (authorisation t) (toURI $ floatingIpsEndpoint </> show ip) >> return Nothing+                          in (r, w))+                  (authToken (ask w))++doAction :: (ComonadEnv ToolConfiguration w, Monad m) => w a -> IP -> IPAction -> (RESTT m (Result (ActionResult IPActionType)), w a)+doAction w ip action = maybe (return $ error "no authentication token defined", w)+                       (\ t -> let r = postJSONWith (authorisation t) (toURI $ floatingIpsEndpoint </> show ip </> "actions") (toJSON action)+                                       >>= return . fromResponse "action"+                               in (r, w))+                       (authToken (ask w))++ipCommandsInterpreter :: (MonadIO m, ComonadEnv ToolConfiguration w) => w a -> CoIPCommands (RESTT m) (w a)+ipCommandsInterpreter = CoIPCommands+                        <$> queryList (Proxy :: Proxy FloatingIP)+                        <*> doCreateIP+                        <*> doDeleteIP+                        <*> doAction+
src/Network/DO/Net.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE OverloadedStrings     #-} {-# LANGUAGE RecordWildCards       #-} {-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-}  -- | Interpreter for accessing DO API through the web using [wreq http://www.serpentine.com/wreq]. module Network.DO.Net(mkDOClient) where@@ -18,9 +19,12 @@ import           Prelude                      as P  import           Network.DO.Commands+import           Network.DO.Domain import           Network.DO.Droplets.Commands import           Network.DO.Droplets.Net+import           Network.DO.IP import           Network.DO.Net.Common+import           Network.DO.Pairing import           Network.DO.Types             as DO hiding (URI) import           Network.REST @@ -74,10 +78,17 @@                   <*> queryList (Proxy :: Proxy Image)                   <*> queryList (Proxy :: Proxy Region) -mkDOClient :: (MonadIO m) => ToolConfiguration -> CofreeT (Product (CoDO (RESTT m)) (CoDropletCommands (RESTT m))) (Env ToolConfiguration) (RESTT m ())+mkDOClient :: (MonadIO m) => ToolConfiguration+           -> CofreeT (CoDO (RESTT m) :*: CoDropletCommands (RESTT m) :*: CoIPCommands (RESTT m) :*: CoDomainCommands (RESTT m)) (Env ToolConfiguration) (RESTT m ()) mkDOClient config = coiterT next start   where     next = Pair            <$> genericCommands-           <*> dropletCommandsInterpreter+           <*> (Pair+                 <$> dropletCommandsInterpreter+                 <*> (Pair+                      <$> ipCommandsInterpreter+                      <*> dnsCommandsInterpreter+                     )+               )     start = env config (return ())
src/Network/DO/Net/Common.hs view
@@ -1,23 +1,17 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, OverloadedStrings #-} module Network.DO.Net.Common where  import           Control.Comonad.Env.Class (ComonadEnv, ask)-import           Control.Lens              ((&), (.~))-import           Data.Aeson                as A+import           Data.Aeson                as A hiding (Result) import qualified Data.Aeson.Types          as A-import           Data.ByteString.Char8     (pack) import qualified Data.HashMap.Strict       as H import           Data.Maybe-import           Data.Monoid import           Data.Proxy import           Data.Text                 (Text) import qualified Data.Vector               as V-import           Network.DO.Types          as DO hiding (URI)+import           Network.DO.Types          as DO hiding (URI, error) import           Network.REST import           Network.URI               (URI, parseURI)-import           Network.Wreq              hiding (Proxy) import           Prelude                   as P  rootURI :: String@@ -31,16 +25,13 @@ s </> s'        = s ++ "/" ++ s'  toURI :: String -> URI-toURI = fromJust . parseURI+toURI s = maybe (P.error $ "cannot parse URI from " ++ s) id $ parseURI s  toList :: (FromJSON a) => Text -> Value -> [a] toList k (Object o) = let Array boxes = o  H.! k                       in mapMaybe (A.parseMaybe parseJSON) (V.toList boxes) toList _  _         = [] -authorisation :: String -> Options-authorisation t = defaults & header "Authorization" .~ ["Bearer " <> pack t]- class Listable a where   listEndpoint :: Proxy a -> String   listField :: Proxy a -> Text@@ -51,3 +42,7 @@                         in (droplets, w))                 (authToken (ask w)) +-- |Extract a typed result from a JSON output+fromResponse :: (FromJSON a) => Text -> Either String Value -> Result a+fromResponse key (Right (Object b)) = either error (Right . id) $ A.parseEither parseJSON (b H.! key)+fromResponse _   v                  = error $ "cannot decode JSON value to a FloatingIP " ++ show v
src/Network/DO/Pairing.hs view
@@ -1,25 +1,42 @@-{-# LANGUAGE FlexibleInstances      #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE KindSignatures         #-}-{-# LANGUAGE MultiParamTypeClasses  #-}-{-# LANGUAGE RankNTypes             #-}-{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds                 #-}+{-# LANGUAGE FlexibleInstances         #-}+{-# LANGUAGE FunctionalDependencies    #-}+{-# LANGUAGE InstanceSigs              #-}+{-# LANGUAGE KindSignatures            #-}+{-# LANGUAGE MultiParamTypeClasses     #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE PolyKinds                 #-}+{-# LANGUAGE RankNTypes                #-}+{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE TypeOperators             #-}+{-# LANGUAGE UndecidableInstances      #-} module Network.DO.Pairing (Pairing(..)                , PairingM(..)+               , (:+:), (:*:)                , pairEffect                , pairEffectM                , pairEffect'                , injr, injl+               , injrl, injrr+               , injrrl, injrrr                ) where  import           Control.Comonad              (Comonad, extract) import           Control.Comonad.Trans.Cofree (CofreeT, unwrap) import           Control.Monad.Trans.Free     (FreeF (..), FreeT, liftF,                                                runFreeT)-import           Data.Functor.Sum import           Data.Functor.Identity        (Identity (..)) import           Data.Functor.Product+import           Data.Functor.Sum +type a :+: b = Sum a b++infixr 1 :+:++type a :*: b = Product a b++infixr 2 :*:+ class (Functor f, Functor g) => Pairing f g where   pair :: (a -> b -> r) -> f a -> g b -> r @@ -38,19 +55,31 @@ instance (Monad m) => PairingM ((,) (m a)) ((->) a) m where   pairM p (ma, b) g = ma >>= \ a -> p b (g a) -instance (Monad m, PairingM f h m, PairingM g k m) => PairingM (Sum f g) (Product h k) m where+instance (Monad m, PairingM f h m, PairingM g k m) => PairingM (f :+: g) (h :*: k) m where   pairM p (InL f) (Pair h _)  = pairM p f h   pairM p (InR g) (Pair _ k) = pairM p g k -instance (Monad m, PairingM h f m, PairingM k g m) => PairingM (Product h k) (Sum f g) m where+instance (Monad m, PairingM h f m, PairingM k g m) => PairingM (h :*: k) (f :+: g) m where   pairM p (Pair h _) (InL f)  = pairM p h f   pairM p (Pair _ k) (InR g) = pairM p k g -injl :: (Monad m, Functor f, Functor g) => f a -> FreeT (Sum f g) m a+injl :: (Monad m, Functor f, Functor g) => f a -> FreeT (f :+: g) m a injl = liftF . InL -injr :: (Monad m, Functor f, Functor g) => g a -> FreeT (Sum f g) m a+injr :: (Monad m, Functor f, Functor g) => g a -> FreeT (f :+: g) m a injr = liftF . InR++injrl :: (Monad m, Functor f, Functor g, Functor h) => g a -> FreeT (f :+: g :+: h) m a+injrl = liftF . InR . InL++injrr :: (Monad m, Functor f, Functor g, Functor h) => h a -> FreeT (f :+: g :+: h) m a+injrr = liftF . InR . InR++injrrl :: (Monad m, Functor f, Functor g, Functor h, Functor k) => h a -> FreeT (f :+: g :+: h :+: k) m a+injrrl = liftF . InR . InR . InL++injrrr :: (Monad m, Functor f, Functor g, Functor h, Functor k) => k a -> FreeT (f :+: g :+: h :+: k) m a+injrrr = liftF . InR . InR . InR  pairEffect :: (Pairing f g, Comonad w, Monad m)            => (a -> b -> r) -> CofreeT f w a -> FreeT g m b -> m r
src/Network/DO/Pretty.hs view
@@ -81,9 +81,35 @@                     nest 5 (pretty szPrice_Hourly <> text "$/h, " <> pretty szPrice_Monthly <> text "$/mo" ) $$                     nest 5 (hcat $ punctuate (char ',') $ map pretty szRegions) -instance Pretty ActionResult where+instance (Pretty r) => Pretty (ActionResult r) where   pretty ActionResult{..} = brackets (integer actionId) <+> pretty actionStartedAt <+> text "->" <+> pretty actionCompletedAt $$-                            (nest 5 $ integer actionResourceId <+> text (show actionType) <> char ':' <+> text (show actionStatus))+                            (nest 5 $ integer actionResourceId <+> pretty actionType <> char ':' <+> text (show actionStatus))++instance Pretty DropletActionType++instance Pretty FloatingIP where+  pretty FloatingIP{..} =+    case floatingDroplet of+      Nothing -> ipAndRegion <+> text "?"+      Just d  -> ipAndRegion <+> integer (dropletId d) <> char '/' <> text (name d)+    where+      ipAndRegion = pretty floatingIp <> text "/" <>  text (regionSlug floatingRegion) <+> text "->"++instance Pretty IPActionType++instance Pretty Domain where+  pretty Domain{..} = text (domain domainName) $$+                      maybe mempty (nest 5 . vcat . map text .lines) zone_file++instance Pretty DomainRecord where+  pretty DomainRecord{..} = brackets (integer recordId) <+> pretty recordType <+> text recordName <+> text recordData+                            <+> prettyInt recordPriority+                            <+> prettyInt recordPort+                            <+> prettyInt recordWeight+    where+      prettyInt = maybe (char '-') int++instance Pretty DNSType  outputResult :: (Pretty a, MonadIO m) => a -> m  () outputResult = liftIO . putStrLn . render . pretty
src/Network/DO/Types.hs view
@@ -17,10 +17,10 @@ import           Data.IP import           Data.List         (elemIndex) import           Data.Monoid       ((<>))-import           Data.Text         (unpack)+import           Data.Text         (pack, unpack) import           Data.Time         (UTCTime) import           GHC.Generics-+import qualified Text.Parsec       as P  type AuthToken = String @@ -73,7 +73,6 @@                         ", regionAvailable = " <> show regionAvailable <>                         "}" - instance FromJSON Region where   parseJSON (String s) = return $ RegionSlug (unpack s)   parseJSON (Object o) = if H.null o@@ -85,7 +84,6 @@                               <*> o .: "available"   parseJSON e          = failParse e - -- | String representation of size slugs -- This maps to corresponding expected JSON string value. sizeSlugs :: [String]@@ -201,6 +199,9 @@   parseJSON (String s) = return $ read $ unpack s   parseJSON e          = fail $ "cannot parse IP " <> show e +instance ToJSON IP where+  toJSON = String . pack . show+ instance FromJSON NetType where   parseJSON (String s) = case s of                           "public" -> return Public@@ -370,23 +371,22 @@    parseJSON e          = failParse e - -- * Droplets Actions  -- | Type of action status -- This is returned when action is initiated or when status of some action is requested -data ActionResult = ActionResult { actionId           :: Id-                                 , actionStatus       :: ActionStatus-                                 , actionType         :: ActionType-                                 , actionStartedAt    :: Maybe Date-                                 , actionCompletedAt  :: Maybe Date-                                 , actionResourceId   :: Id-                                 , actionResourceType :: String-                                 , actionRegionSlug   :: Region-                                 } deriving (Show)+data ActionResult result = ActionResult { actionId           :: Id+                                        , actionStatus       :: ActionStatus+                                        , actionType         :: result+                                        , actionStartedAt    :: Maybe Date+                                        , actionCompletedAt  :: Maybe Date+                                        , actionResourceId   :: Id+                                        , actionResourceType :: String+                                        , actionRegionSlug   :: Region+                                        } deriving (Show) -instance FromJSON ActionResult where+instance (FromJSON r) => FromJSON (ActionResult r) where   parseJSON (Object o) = ActionResult                          <$> o .: "id"                          <*> o .: "status"@@ -411,12 +411,12 @@                           _             -> fail $ "unknown action status " ++ show s   parseJSON v          = fail $ "cannot parse action status " ++ show v -data ActionType = PowerOff+data DropletActionType = PowerOff                 | PowerOn                 | MakeSnapshot                 deriving (Show) -instance FromJSON ActionType where+instance FromJSON DropletActionType where   parseJSON (String s) = case s of                           "power_off" -> return PowerOff                           "power_on"  -> return PowerOn@@ -424,7 +424,7 @@                           _           -> fail $ "unknown action type " ++ show s   parseJSON v          = fail $ "cannot parse action type " ++ show v -instance ToJSON ActionType where+instance ToJSON DropletActionType where   toJSON PowerOff = String "power_off"   toJSON PowerOn  = String "power_on"   toJSON MakeSnapshot = String "snapshot"@@ -444,9 +444,25 @@ -- |Type of Domain zones -- -- https://developers.digitalocean.com/documentation/v2/#domains-data Domain = Domain { domainName :: String-                     , domainTTL  :: Int-                     , zone_file  :: String++newtype DomainName = DomainName { domain :: String }++instance Show DomainName where+  show = domain++instance Read DomainName where+  readsPrec _ s = [(DomainName s,[])]++instance FromJSON DomainName where+  parseJSON (String s) = pure $ DomainName $ unpack s+  parseJSON e          = failParse e++instance ToJSON DomainName where+  toJSON (DomainName n) = String (pack n)++data Domain = Domain { domainName :: DomainName+                     , domainTTL  :: Maybe Int+                     , zone_file  :: Maybe String                      } deriving (Show)  instance FromJSON Domain where@@ -457,6 +473,13 @@    parseJSON e          = failParse e +data DomainConfig = DomainConfig DomainName IP++instance ToJSON DomainConfig where+  toJSON (DomainConfig name ip) = object [ "name" .= name+                                         , "ip_address" .= ip+                                         ]+ -- | Enumeration of possible DNS records types data DNSType = A | CNAME | TXT | PTR | SRV | NS | AAAA | MX              deriving (Show, Read, Generic)@@ -471,9 +494,9 @@                                  , recordType     :: DNSType                                  , recordName     :: String                                  , recordData     :: String-                                 , recordPriority :: Maybe Double+                                 , recordPriority :: Maybe Int                                  , recordPort     :: Maybe Int-                                 , recordWeight   :: Maybe Double+                                 , recordWeight   :: Maybe Int                                  } deriving (Show)  @@ -488,6 +511,105 @@                          <*> o .: "weight"    parseJSON e          = failParse e++instance ToJSON DomainRecord where+  toJSON DomainRecord{..} = object [ "type" .= recordType+                                   , "name" .= recordName+                                   , "data" .= recordData+                                   , "priority" .= recordPriority+                                   , "port" .= recordPort+                                   , "weight" .= recordWeight+                                   ]++parseRecord :: String -> Result DomainRecord+parseRecord s =+  case P.parse recordParser "" s of+    Left e  -> Left (Error $ show e)+    Right r -> Right r+  where+    recordParser :: P.Parsec String s DomainRecord+    recordParser = do+      t <- typeParser+      P.spaces+      n <- nameParser+      P.spaces+      d <- dataParser+      (prio, port, wei) <- recordAttributes t+      return $ DomainRecord 0 t n d prio port wei++    typeParser :: P.Parsec String s DNSType+    typeParser = P.choice [ rtype A , rtype CNAME , rtype TXT , rtype PTR , rtype SRV , rtype NS , rtype AAAA , rtype MX ]++    rtype :: DNSType -> P.Parsec String s DNSType+    rtype t = P.string (show t) >> return t++    nameParser :: P.Parsec String s String+    nameParser = P.many1 (P.lower P.<|> P.char '.')++    dataParser :: P.Parsec String s String+    dataParser = P.many1 (P.alphaNum P.<|> P.oneOf [ '.' ])++    recordAttributes :: DNSType -> P.Parsec String s (Maybe Int, Maybe Int, Maybe Int)+    recordAttributes SRV =  (,,) <$>+                            (Just <$> number) <*>+                            (Just <$> number) <*>+                            (Just <$> number)+    recordAttributes MX  =  (,,) <$>+                            (Just <$> number) <*>+                            pure Nothing <*>+                            pure Nothing+    recordAttributes _   = (,,) <$>+                           pure Nothing <*>+                           pure Nothing <*>+                           pure Nothing+    number :: P.Parsec String s Int+    number = P.spaces >> read <$> P.many1 P.digit++-- | Floating IPs+-- https://developers.digitalocean.com/documentation/v2/#floating-ips++data FloatingIP = FloatingIP { floatingIp      :: IP+                             , floatingDroplet :: Maybe Droplet+                             , floatingRegion  :: Region+                             } deriving (Show)++instance FromJSON FloatingIP where+  parseJSON (Object o) = FloatingIP+                         <$> o .: "ip"+                         <*> o .:? "droplet"+                         <*> o .: "region"++  parseJSON e          = failParse e++data FloatingIPTarget = TargetRegion Slug+                      | TargetDroplet Id+                        deriving (Show)++instance ToJSON FloatingIPTarget where+  toJSON (TargetRegion r)  = object [ "region" .= r ]+  toJSON (TargetDroplet i) = object [ "droplet_id" .= i ]++data IPAction = AssignIP Id+              | UnassignIP+  deriving (Show, Read)++instance ToJSON IPAction where+  toJSON (AssignIP did) = object [ "type" .= ("assign" :: String)+                                 , "droplet_id" .= did+                                 ]+  toJSON UnassignIP     = object [ "type" .= ("unassign" :: String)]++data IPActionType = Assign+                  | Unassign+                deriving (Show)++instance FromJSON IPActionType where+  parseJSON (String s) = case s of+                          "assign_ip" -> return Assign+                          "unassign_ip"  -> return Unassign+                          _           -> fail $ "unknown action type " ++ show s+  parseJSON v          = failParse v+  failParse :: (Show a1, Monad m) => a1 -> m a failParse e = fail $ "cannot parse " <> show e
src/Network/REST.hs view
@@ -1,11 +1,9 @@-{-# LANGUAGE DeriveFunctor       #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveFunctor, ScopedTypeVariables #-} -- | Functor and low-level mechanism to interact with a server using Wreq and JSON values.-module Network.REST(module Network.REST.Commands-                   ,module Network.REST.Wreq+module Network.REST(module M                    ,ssh) where -import           Network.REST.Commands-import           Network.REST.Wreq+import           Network.REST.Commands as M+import           Network.REST.Conduit  as M -- TODO temporary kludge import           Network.SSH
src/Network/REST/Commands.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE DeriveFunctor       #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveFunctor, OverloadedStrings, ScopedTypeVariables #-} -- | Functor and low-level mechanism to interact with a server using "REST" API. ----- This module exposes @Net@ functor which can be implemented using various stack.+-- This module exposes @REST@ functor which can be implemented using various stack. -- In particular, we provide @Network.REST.Wreq@ module as a wreq-based implementation -- and a test-only implementation which is used to check correctness of higher-level -- code, in particular in relation with JSON serialization.@@ -10,8 +9,14 @@  import           Control.Monad.Trans.Free (FreeT (..), liftF) import           Data.Aeson               (Value)+import           Data.Monoid+import           Data.Text                (Text, pack) import           Network.URI              (URI)-import           Network.Wreq             (Options)++data Options = Header Text [ Text ]++authorisation :: String -> Options+authorisation t = Header "Authorization" [ "Bearer " <> pack t]  data REST a = Get URI (Value -> a)            | Post URI Value (Maybe Value -> a)
+ src/Network/REST/Conduit.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE ScopedTypeVariables #-}+-- | Implementation of @REST@ interface based on <https://haskell-lang.org/library/http-client http-conduit>+-- standard library+module Network.REST.Conduit where++import           Control.Concurrent       (threadDelay)+import           Control.Monad.Trans.Free+import           Data.Aeson               (Value, decode, eitherDecode)+import           Data.CaseInsensitive     (mk)+import           Data.Functor             (void)+import           Data.Text.Encoding       (encodeUtf8)+import           Network.HTTP.Simple+import           Network.REST.Commands+import           Network.URI++-- | An implementation of @REST@ functor based on http-client and @IO@+runConduit :: RESTT IO r -> IO r+runConduit r = do+  mr <- runFreeT r+  step mr+    where+      asString :: URI -> String+      asString = ($ "") . uriToString id++      step (Pure value)       = return value++      step (Free (WaitFor delay message k))  = do+        putStrLn message+        threadDelay delay+        runConduit k++      step (Free (Get uri k)) = do+        request <- parseRequest $ "GET " ++ asString uri+        response <- httpJSON request+        let value = getResponseBody response :: Value+        runConduit (k value)++      step (Free (GetWith opts uri k)) = do+        request <- parseRequest $ "GET " ++ asString uri+        response <- httpJSON $ options opts request+        let value = getResponseBody response :: Value+        runConduit (k value)++      step (Free (DeleteWith opts uri k)) = do+        request <- parseRequest $ "DELETE " ++ asString uri+        void $ httpLBS $ options opts request+        runConduit k++      step (Free (Post uri val k)) = do+        request <- parseRequest $ "POST " ++ asString uri+        response <- httpLbs $ setRequestBodyJSON val request+        let value = decode $ getResponseBody response+        runConduit (k value)++      step (Free (PostWith opts uri val k)) = do+        request <- parseRequest $ "POST " ++ asString uri+        response <- httpLbs $ options opts $ setRequestBodyJSON val request+        let value :: Either String Value = eitherDecode $ getResponseBody response+        runConduit (k value)+++options :: Options -> Request -> Request+options (Header h c) = setRequestHeader (mk $ encodeUtf8 h) (map encodeUtf8 c)
− src/Network/REST/Wreq.hs
@@ -1,54 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}--- | Implementation of @Net@ interface based on <http://wreq.io wreq>-module Network.REST.Wreq where--import           Control.Concurrent       (threadDelay)-import           Control.Lens             ((^.))-import           Control.Monad.Trans.Free-import           Data.Aeson               (Value, decode, eitherDecode)-import           Data.ByteString.Lazy     (ByteString)-import           Network.REST.Commands-import           Network.URI-import           Network.Wreq---- | An implementation of @Net@ functor based on wreq and @IO@-runWreq :: RESTT IO r -> IO r-runWreq r = do-  mr <- runFreeT r-  step mr-    where-      asString :: URI -> String-      asString = ($ "") . uriToString id--      step (Pure value)       = return value--      step (Free (WaitFor delay message k))  = do-        putStrLn message-        threadDelay delay-        runWreq k--      step (Free (Get uri k)) = do-        b <- asJSON =<< get (asString uri) :: IO (Response Value)-        let value = b ^. responseBody-        runWreq (k value)--      step (Free (GetWith opts uri k)) = do-        b <- asJSON =<< getWith opts (asString uri) :: IO (Response Value)-        let value = b ^. responseBody-        runWreq (k value)--      step (Free (DeleteWith opts uri k)) = do-        _ <- deleteWith opts (asString uri) :: IO (Response ByteString)-        runWreq k--      step (Free (Post uri val k)) = do-        resp <- post (asString uri) val :: IO (Response ByteString)-        let value :: Maybe Value = decode $ resp ^. responseBody-        runWreq (k value)--      step (Free (PostWith opts uri val k)) = do-        resp <- postWith opts (asString uri) val :: IO (Response ByteString)-        let value :: Either String Value = eitherDecode $ resp ^. responseBody-        runWreq (k value)--
stack.yaml view
@@ -1,4 +1,4 @@ flags: {} packages: - '.'-resolver: lts-7.3+resolver: lts-8.8