google-cloud-compute-0.1.0.0: src/Google/Cloud/Compute/Instance.hs
{-|
Module : Google.Cloud.Compute.Instance
Copyright : (c) 2025 Tushar
License : MIT
Maintainer :
Stability : experimental
This module provides types and functions for interacting with Google Cloud Platform (GCP)
Compute Engine instances. It supports common operations such as listing, creating, deleting,
starting, and stopping instances, along with detailed configuration of instance properties.
All functions communicate with the GCP Compute Engine API v1 and return either an error
message ('Left') or a parsed response object ('Right').
For more information on the underlying API, see:
<https://cloud.google.com/compute/docs/reference/rest/v1/instances GCP Compute Instances Documentation>
-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module Google.Cloud.Compute.Instance
( -- Data Types
InstanceMetadata (..)
, InstanceDeleteResp (..)
, InstanceStartResponse (..)
, RunDuration (..)
, OnInstanceTerminationAction (..)
, Scheduling (..)
, InstanceTerminationAction (..)
, NodeAffinity (..)
, Warning (..)
, GuestAccelerator (..)
, ReservationAffinity (..)
, DisplayDevice (..)
, ShieldedInstanceConfig (..)
, ShieldedInstanceIntegrityPolicy (..)
, UpdateAutoLearnPolicy (..)
, ConfidentialInstanceConfig (..)
, InstanceEncryptionKey (..)
, NetworkPerformanceConfig (..)
, ResourceStatus (..)
, InstanceList (..)
, Error_ (..)
, ErrorDetail (..)
, DetailedError (..)
, ErrorInfo (..)
, QuotaInfo (..)
, Help (..)
, Link (..)
, LocalizedMessage (..)
-- Query Parameter Types
, ListInstancesQuery (..)
, RequestIdQuery (..)
, InsertInstanceOps (..)
-- Functions
, listInstances
, deleteInstance
, startInstance
, stopInstance
, insertInstance
-- Default Query Constructors
, defaultListInstancesQuery
, defaultInsertInstanceOps
, defaultRequestIdQuery
)
where
import Data.Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import Data.Maybe (catMaybes)
import Data.Text (Text)
import Google.Cloud.Common.Core
import Google.Cloud.Compute.Common
import qualified Data.Map.Strict as Map
-- ** Query Parameter Types**
-- | Query parameters for listing instances
data ListInstancesQuery = ListInstancesQuery
{ filter_ :: Maybe String
-- ^ Filter expression for filtering listed instances
, maxResults :: Maybe Int
-- ^ Maximum number of results per page
, orderBy :: Maybe String
-- ^ Sort order for results
, pageToken :: Maybe String
-- ^ Page token for paginated results
, returnPartialSuccess :: Maybe Bool
-- ^ Whether to return partial success results
}
deriving (Show, Eq)
-- Query parameters for operations with requestId (start, stop, delete)
data RequestIdQuery = RequestIdQuery
{ requestId :: Maybe String
}
deriving (Show, Eq)
-- ** Data Types with Custom JSON Instances**
data RunDuration
= TerminationTime String
deriving (Show, Eq)
instance FromJSON RunDuration where
parseJSON = withObject "RunDuration" $ \o -> TerminationTime <$> o .: "terminationTime"
instance ToJSON RunDuration where
toJSON (TerminationTime t) = object ["terminationTime" .= t]
data OnInstanceTerminationAction = DiscardLocalSsd {discardLocalSsd :: Bool}
deriving (Show, Eq)
instance FromJSON OnInstanceTerminationAction where
parseJSON = withObject "OnInstanceTerminationAction" $ \o -> DiscardLocalSsd <$> o .: "discardLocalSsd"
instance ToJSON OnInstanceTerminationAction where
toJSON (DiscardLocalSsd b) = object ["discardLocalSsd" .= b]
data Scheduling = Scheduling
{ onHostMaintenance :: String
, automaticRestart :: Bool
, preemptible :: Bool
, nodeAffinities :: [NodeAffinity]
, minNodeCpus :: Int
, locationHint :: String
, availabilityDomain :: Int
, provisioningModel :: String
, instanceTerminationAction :: InstanceTerminationAction
}
deriving (Show, Eq)
instance FromJSON Scheduling where
parseJSON = withObject "Scheduling" $ \o ->
Scheduling
<$> o .: "onHostMaintenance"
<*> o .: "automaticRestart"
<*> o .: "preemptible"
<*> o .: "nodeAffinities"
<*> o .: "minNodeCpus"
<*> o .: "locationHint"
<*> o .: "availabilityDomain"
<*> o .: "provisioningModel"
<*> o .: "instanceTerminationAction"
instance ToJSON Scheduling where
toJSON Scheduling {..} =
object
[ "onHostMaintenance" .= onHostMaintenance
, "automaticRestart" .= automaticRestart
, "preemptible" .= preemptible
, "nodeAffinities" .= nodeAffinities
, "minNodeCpus" .= minNodeCpus
, "locationHint" .= locationHint
, "availabilityDomain" .= availabilityDomain
, "provisioningModel" .= provisioningModel
, "instanceTerminationAction" .= instanceTerminationAction
]
data InstanceTerminationAction
= RunDurationRunDuration RunDuration
deriving (Show, Eq)
instance FromJSON InstanceTerminationAction where
parseJSON v = RunDurationRunDuration <$> parseJSON v
instance ToJSON InstanceTerminationAction where
toJSON (RunDurationRunDuration rd) = toJSON rd
data NodeAffinity = NodeAffinity
{ key :: String
, operator :: String
, values :: [String]
}
deriving (Show, Eq)
instance FromJSON NodeAffinity where
parseJSON = withObject "NodeAffinity" $ \o ->
NodeAffinity
<$> o .: "key"
<*> o .: "operator"
<*> o .: "values"
instance ToJSON NodeAffinity where
toJSON NodeAffinity {..} =
object
[ "key" .= key
, "operator" .= operator
, "values" .= values
]
data Warning = Warning
{ code :: String
, message :: String
}
deriving (Show, Eq)
instance FromJSON Warning where
parseJSON = withObject "Warning" $ \o ->
Warning
<$> o .: "code"
<*> o .: "message"
instance ToJSON Warning where
toJSON Warning {..} =
object
[ "code" .= code
, "message" .= message
]
-- | Metadata representing a Compute Engine instance
data InstanceMetadata = InstanceMetadata
{ cpuPlatform :: Maybe String
-- ^ The CPU platform used by this instance
, labelFingerprint :: Maybe Text
-- ^ Fingerprint of the label metadata
, instanceEncryptionKey :: Maybe InstanceEncryptionKey
-- ^ Encryption key configuration for the instance
, minCpuPlatform :: Maybe Text
-- ^ Minimum CPU platform required by the instance
, guestAccelerators :: Maybe [GuestAccelerator]
-- ^ List of guest accelerators attached to the instance
, startRestricted :: Maybe Bool
-- ^ Whether instance start is restricted due to storage issues
, deletionProtection :: Maybe Bool
-- ^ Whether deletion protection is enabled
, resourcePolicies :: Maybe [Text]
-- ^ Resource policies applied to the instance
, sourceMachineImage :: Maybe Text
-- ^ Source machine image used to create the instance
, reservationAffinity :: Maybe ReservationAffinity
-- ^ Reservation affinity configuration
, hostname :: Maybe Text
-- ^ Custom hostname assigned to the instance
, displayDevice :: Maybe DisplayDevice
-- ^ Display device configuration
, shieldedInstanceConfig :: Maybe ShieldedInstanceConfig
-- ^ Shielded VM configuration
, shieldedInstanceIntegrityPolicy :: Maybe ShieldedInstanceIntegrityPolicy
-- ^ Integrity policy for shielded instances
, sourceMachineImageEncryptionKey :: Maybe InstanceEncryptionKey
-- ^ Encryption key for source machine image
, confidentialInstanceConfig :: Maybe ConfidentialInstanceConfig
-- ^ Confidential computing configuration
, fingerprint :: Maybe Text
-- ^ Unique fingerprint for resource metadata
, privateIpv6GoogleAccess :: Maybe Text
-- ^ Private IPv6 Google access configuration
, lastStartTimestamp :: Maybe String
-- ^ Last start timestamp in RFC3339 format
, lastStopTimestamp :: Maybe String
-- ^ Last stop timestamp in RFC3339 format
, lastSuspendedTimestamp :: Maybe String
-- ^ Last suspension timestamp in RFC3339 format
, satisfiesPzs :: Maybe Bool
-- ^ Whether instance satisfies zone separation policy
, satisfiesPzi :: Maybe Bool
-- ^ Whether instance satisfies instance separation policy
, resourceStatus :: Maybe ResourceStatus
-- ^ Current resource status of the instance
, networkPerformanceConfig :: Maybe NetworkPerformanceConfig
-- ^ Network performance configuration
, keyRevocationActionType :: Maybe Text
-- ^ Type of key revocation action if applicable
}
deriving (Show, Eq)
instance FromJSON InstanceMetadata where
parseJSON = withObject "InstanceMetadata" $ \o ->
InstanceMetadata
<$> o .:? "cpuPlatform"
<*> o .:? "labelFingerprint"
<*> o .:? "instanceEncryptionKey"
<*> o .:? "minCpuPlatform"
<*> o .:? "guestAccelerators"
<*> o .:? "startRestricted"
<*> o .:? "deletionProtection"
<*> o .:? "resourcePolicies"
<*> o .:? "sourceMachineImage"
<*> o .:? "reservationAffinity"
<*> o .:? "hostname"
<*> o .:? "displayDevice"
<*> o .:? "shieldedInstanceConfig"
<*> o .:? "shieldedInstanceIntegrityPolicy"
<*> o .:? "sourceMachineImageEncryptionKey"
<*> o .:? "confidentialInstanceConfig"
<*> o .:? "fingerprint"
<*> o .:? "privateIpv6GoogleAccess"
<*> o .:? "lastStartTimestamp"
<*> o .:? "lastStopTimestamp"
<*> o .:? "lastSuspendedTimestamp"
<*> o .:? "satisfiesPzs"
<*> o .:? "satisfiesPzi"
<*> o .:? "resourceStatus"
<*> o .:? "networkPerformanceConfig"
<*> o .:? "keyRevocationActionType"
instance ToJSON InstanceMetadata where
toJSON im =
object $
catMaybes
[ ("cpuPlatform" .=) <$> cpuPlatform im
, ("labelFingerprint" .=) <$> labelFingerprint im
, ("instanceEncryptionKey" .=) <$> instanceEncryptionKey im
, ("minCpuPlatform" .=) <$> minCpuPlatform im
, ("guestAccelerators" .=) <$> guestAccelerators im
, ("startRestricted" .=) <$> startRestricted im
, ("deletionProtection" .=) <$> deletionProtection im
, ("resourcePolicies" .=) <$> resourcePolicies im
, ("sourceMachineImage" .=) <$> sourceMachineImage im
, ("reservationAffinity" .=) <$> reservationAffinity im
, ("hostname" .=) <$> hostname im
, ("displayDevice" .=) <$> displayDevice im
, ("shieldedInstanceConfig" .=) <$> shieldedInstanceConfig im
, ("shieldedInstanceIntegrityPolicy" .=) <$> shieldedInstanceIntegrityPolicy im
, ("sourceMachineImageEncryptionKey" .=) <$> sourceMachineImageEncryptionKey im
, ("confidentialInstanceConfig" .=) <$> confidentialInstanceConfig im
, ("fingerprint" .=) <$> fingerprint im
, ("privateIpv6GoogleAccess" .=) <$> privateIpv6GoogleAccess im
, ("lastStartTimestamp" .=) <$> lastStartTimestamp im
, ("lastStopTimestamp" .=) <$> lastStopTimestamp im
, ("lastSuspendedTimestamp" .=) <$> lastSuspendedTimestamp im
, ("satisfiesPzs" .=) <$> satisfiesPzs im
, ("satisfiesPzi" .=) <$> satisfiesPzi im
, ("resourceStatus" .=) <$> resourceStatus im
, ("networkPerformanceConfig" .=) <$> networkPerformanceConfig im
, ("keyRevocationActionType" .=) <$> keyRevocationActionType im
]
data GuestAccelerator = GuestAccelerator
{ acceleratorType :: Text
, acceleratorCount :: Int
}
deriving (Show, Eq)
instance FromJSON GuestAccelerator where
parseJSON = withObject "GuestAccelerator" $ \o ->
GuestAccelerator
<$> o .: "acceleratorType"
<*> o .: "acceleratorCount"
instance ToJSON GuestAccelerator where
toJSON ga =
object
[ "acceleratorType" .= acceleratorType ga
, "acceleratorCount" .= acceleratorCount ga
]
data ReservationAffinity = ReservationAffinity
{ consumeReservationType :: Maybe Text
, key :: Maybe Text
, values :: Maybe [Text]
}
deriving (Show, Eq)
instance FromJSON ReservationAffinity where
parseJSON = withObject "ReservationAffinity" $ \o ->
ReservationAffinity
<$> o .:? "consumeReservationType"
<*> o .:? "key"
<*> o .:? "values"
instance ToJSON ReservationAffinity where
toJSON ReservationAffinity {..} =
object $
catMaybes
[ ("consumeReservationType" .=) <$> consumeReservationType
, ("key" .=) <$> key
, ("values" .=) <$> values
]
data DisplayDevice = DisplayDevice
{ enableDisplay :: Bool
}
deriving (Show, Eq)
instance FromJSON DisplayDevice where
parseJSON = withObject "DisplayDevice" $ \o ->
DisplayDevice
<$> o .: "enableDisplay"
instance ToJSON DisplayDevice where
toJSON dd = object ["enableDisplay" .= enableDisplay dd]
data ShieldedInstanceConfig = ShieldedInstanceConfig
{ enableSecureBoot :: Bool
, enableVtpm :: Bool
, enableIntegrityMonitoring :: Bool
}
deriving (Show, Eq)
instance FromJSON ShieldedInstanceConfig where
parseJSON = withObject "ShieldedInstanceConfig" $ \o ->
ShieldedInstanceConfig
<$> o .: "enableSecureBoot"
<*> o .: "enableVtpm"
<*> o .: "enableIntegrityMonitoring"
instance ToJSON ShieldedInstanceConfig where
toJSON sic =
object
[ "enableSecureBoot" .= enableSecureBoot sic
, "enableVtpm" .= enableVtpm sic
, "enableIntegrityMonitoring" .= enableIntegrityMonitoring sic
]
data ShieldedInstanceIntegrityPolicy = ShieldedInstanceIntegrityPolicy
{ policy :: Maybe UpdateAutoLearnPolicy
}
deriving (Show, Eq)
instance FromJSON ShieldedInstanceIntegrityPolicy where
parseJSON = withObject "ShieldedInstanceIntegrityPolicy" $ \o ->
ShieldedInstanceIntegrityPolicy
<$> o .:? "policy"
instance ToJSON ShieldedInstanceIntegrityPolicy where
toJSON sip =
object $
catMaybes
[("policy" .=) <$> policy sip]
data UpdateAutoLearnPolicy = UpdateAutoLearnPolicy {updateAutoLearnPolicy :: Bool}
deriving (Show, Eq)
instance FromJSON UpdateAutoLearnPolicy where
parseJSON = withObject "UpdateAutoLearnPolicy" $ \o ->
UpdateAutoLearnPolicy
<$> o .: "updateAutoLearnPolicy"
instance ToJSON UpdateAutoLearnPolicy where
toJSON uap = object ["updateAutoLearnPolicy" .= updateAutoLearnPolicy uap]
data ConfidentialInstanceConfig = ConfidentialInstanceConfig
{ enableConfidentialCompute :: Maybe Bool
, confidentialInstanceType :: Maybe Text
}
deriving (Show, Eq)
instance FromJSON ConfidentialInstanceConfig where
parseJSON = withObject "ConfidentialInstanceConfig" $ \o ->
ConfidentialInstanceConfig
<$> o .:? "enableConfidentialCompute"
<*> o .:? "confidentialInstanceType"
instance ToJSON ConfidentialInstanceConfig where
toJSON cic =
object $
catMaybes
[ ("enableConfidentialCompute" .=) <$> enableConfidentialCompute cic
, ("confidentialInstanceType" .=) <$> confidentialInstanceType cic
]
data InstanceEncryptionKey = InstanceEncryptionKey
{ sha256 :: Maybe Text
, kmsKeyServiceAccount :: Maybe Text
, rawKey :: Maybe Text
, rsaEncryptedKey :: Maybe Text
, kmsKeyName :: Maybe Text
}
deriving (Show, Eq)
instance FromJSON InstanceEncryptionKey where
parseJSON = withObject "InstanceEncryptionKey" $ \o ->
InstanceEncryptionKey
<$> o .:? "sha256"
<*> o .:? "kmsKeyServiceAccount"
<*> o .:? "rawKey"
<*> o .:? "rsaEncryptedKey"
<*> o .:? "kmsKeyName"
instance ToJSON InstanceEncryptionKey where
toJSON iek =
object $
catMaybes
[ ("sha256" .=) <$> sha256 iek
, ("kmsKeyServiceAccount" .=) <$> kmsKeyServiceAccount iek
, ("rawKey" .=) <$> rawKey iek
, ("rsaEncryptedKey" .=) <$> rsaEncryptedKey iek
, ("kmsKeyName" .=) <$> kmsKeyName iek
]
data NetworkPerformanceConfig = NetworkPerformanceConfig
{ totalEgressBandwidthTier :: Text
}
deriving (Show, Eq)
instance FromJSON NetworkPerformanceConfig where
parseJSON = withObject "NetworkPerformanceConfig" $ \o ->
NetworkPerformanceConfig
<$> o .: "totalEgressBandwidthTier"
instance ToJSON NetworkPerformanceConfig where
toJSON npc = object ["totalEgressBandwidthTier" .= totalEgressBandwidthTier npc]
data ResourceStatus = ResourceStatus
{ onHostMaintenance :: String
, automaticRestart :: Bool
, preemptible :: Bool
, nodeAffinities :: [NodeAffinity]
, minNodeCpus :: Int
, locationHint :: String
, availabilityDomain :: Int
, provisioningModel :: String
, instanceTerminationAction :: InstanceTerminationAction
}
deriving (Show, Eq)
instance FromJSON ResourceStatus where
parseJSON = withObject "ResourceStatus" $ \o ->
ResourceStatus
<$> o .: "onHostMaintenance"
<*> o .: "automaticRestart"
<*> o .: "preemptible"
<*> o .: "nodeAffinities"
<*> o .: "minNodeCpus"
<*> o .: "locationHint"
<*> o .: "availabilityDomain"
<*> o .: "provisioningModel"
<*> o .: "instanceTerminationAction"
instance ToJSON ResourceStatus where
toJSON ResourceStatus {..} =
object
[ "onHostMaintenance" .= onHostMaintenance
, "automaticRestart" .= automaticRestart
, "preemptible" .= preemptible
, "nodeAffinities" .= nodeAffinities
, "minNodeCpus" .= minNodeCpus
, "locationHint" .= locationHint
, "availabilityDomain" .= availabilityDomain
, "provisioningModel" .= provisioningModel
, "instanceTerminationAction" .= instanceTerminationAction
]
data InstanceList = InstanceList
{ kind :: String
, id_ :: String
, items :: Maybe [InstanceMetadata]
}
deriving (Show, Eq)
instance FromJSON InstanceList where
parseJSON = withObject "InstanceList" $ \o ->
InstanceList
<$> o .: "kind"
<*> o .: "id"
<*> o .: "items"
instance ToJSON InstanceList where
toJSON InstanceList {..} =
object
[ "kind" .= kind
, "id" .= id_
, "items" .= items
]
-- | Response structure for delete instance operation
data InstanceDeleteResp = InstanceDeleteResp
{ kind :: Maybe String
-- ^ Type of resource (always "compute#operation")
, id_ :: Maybe String
-- ^ Unique identifier for the operation
, creationTimestamp :: Maybe String
-- ^ Creation timestamp in RFC3339 format
, name :: Maybe String
, zone :: Maybe String
, clientOperationId :: Maybe String
, operationType :: Maybe String
, targetLink :: Maybe String
, targetId :: Maybe String
, status :: Maybe String
, statusMessage :: Maybe String
, user :: Maybe String
, progress :: Maybe Int
, insertTime :: Maybe String
, startTime :: Maybe String
, endTime :: Maybe String
, error_ :: Maybe Error_
, warnings :: Maybe [Warning]
, httpErrorStatusCode :: Maybe Int
, httpErrorMessage :: Maybe String
, selfLink :: Maybe String
, region :: Maybe String
, description :: Maybe String
, operationGroupId :: Maybe String
}
deriving (Show, Eq)
instance FromJSON InstanceDeleteResp where
parseJSON = withObject "InstanceDeleteResp" $ \o ->
InstanceDeleteResp
<$> o .:? "kind"
<*> o .:? "id"
<*> o .:? "creationTimestamp"
<*> o .:? "name"
<*> o .:? "zone"
<*> o .:? "clientOperationId"
<*> o .:? "operationType"
<*> o .:? "targetLink"
<*> o .:? "targetId"
<*> o .:? "status"
<*> o .:? "statusMessage"
<*> o .:? "user"
<*> o .:? "progress"
<*> o .:? "insertTime"
<*> o .:? "startTime"
<*> o .:? "endTime"
<*> o .:? "error"
<*> o .:? "warnings"
<*> o .:? "httpErrorStatusCode"
<*> o .:? "httpErrorMessage"
<*> o .:? "selfLink"
<*> o .:? "region"
<*> o .:? "description"
<*> o .:? "operationGroupId"
instance ToJSON InstanceDeleteResp where
toJSON InstanceDeleteResp {..} =
object $
catMaybes
[ ("kind" .=) <$> kind
, ("id" .=) <$> id_
, ("creationTimestamp" .=) <$> creationTimestamp
, ("name" .=) <$> name
, ("zone" .=) <$> zone
, ("clientOperationId" .=) <$> clientOperationId
, ("operationType" .=) <$> operationType
, ("targetLink" .=) <$> targetLink
, ("targetId" .=) <$> targetId
, ("status" .=) <$> status
, ("statusMessage" .=) <$> statusMessage
, ("user" .=) <$> user
, ("progress" .=) <$> progress
, ("insertTime" .=) <$> insertTime
, ("startTime" .=) <$> startTime
, ("endTime" .=) <$> endTime
, ("error" .=) <$> error_
, ("warnings" .=) <$> warnings
, ("httpErrorStatusCode" .=) <$> httpErrorStatusCode
, ("httpErrorMessage" .=) <$> httpErrorMessage
, ("selfLink" .=) <$> selfLink
, ("region" .=) <$> region
, ("description" .=) <$> description
, ("operationGroupId" .=) <$> operationGroupId
]
data Error_ = Error_
{ errors :: Maybe [ErrorDetail]
}
deriving (Show, Eq)
instance FromJSON Error_ where
parseJSON = withObject "Error" $ \o -> Error_ <$> o .:? "errors"
instance ToJSON Error_ where
toJSON e = object $ catMaybes [("errors" .=) <$> errors e]
data ErrorDetail = ErrorDetail
{ code :: Maybe String
, location :: Maybe String
, message :: Maybe String
, errorDetails :: Maybe [DetailedError]
}
deriving (Show, Eq)
instance FromJSON ErrorDetail where
parseJSON = withObject "ErrorDetail" $ \o ->
ErrorDetail
<$> o .:? "code"
<*> o .:? "location"
<*> o .:? "message"
<*> o .:? "errorDetails"
instance ToJSON ErrorDetail where
toJSON ErrorDetail {..} =
object $
catMaybes
[ ("code" .=) <$> code
, ("location" .=) <$> location
, ("message" .=) <$> message
, ("errorDetails" .=) <$> errorDetails
]
data DetailedError = DetailedError
{ errorInfo :: Maybe ErrorInfo
, quotaInfo :: Maybe QuotaInfo
, help :: Maybe Help
, localizedMessage :: Maybe LocalizedMessage
}
deriving (Show, Eq)
instance FromJSON DetailedError where
parseJSON = withObject "DetailedError" $ \o ->
DetailedError
<$> o .:? "errorInfo"
<*> o .:? "quotaInfo"
<*> o .:? "help"
<*> o .:? "localizedMessage"
instance ToJSON DetailedError where
toJSON de =
object $
catMaybes
[ ("errorInfo" .=) <$> errorInfo de
, ("quotaInfo" .=) <$> quotaInfo de
, ("help" .=) <$> help de
, ("localizedMessage" .=) <$> localizedMessage de
]
data ErrorInfo = ErrorInfo
{ reason :: Maybe String
, domain :: Maybe String
}
deriving (Show, Eq)
instance FromJSON ErrorInfo where
parseJSON = withObject "ErrorInfo" $ \o ->
ErrorInfo
<$> o .:? "reason"
<*> o .:? "domain"
instance ToJSON ErrorInfo where
toJSON ei =
object $
catMaybes
[ ("reason" .=) <$> reason ei
, ("domain" .=) <$> domain ei
]
data QuotaInfo = QuotaInfo
{ metricName :: Maybe String
, limitName :: Maybe String
, limit :: Maybe Double
, futureLimit :: Maybe Double
, rolloutStatus :: Maybe String
}
deriving (Show, Eq)
instance FromJSON QuotaInfo where
parseJSON = withObject "QuotaInfo" $ \o ->
QuotaInfo
<$> o .:? "metricName"
<*> o .:? "limitName"
<*> o .:? "limit"
<*> o .:? "futureLimit"
<*> o .:? "rolloutStatus"
instance ToJSON QuotaInfo where
toJSON qi =
object $
catMaybes
[ ("metricName" .=) <$> metricName qi
, ("limitName" .=) <$> limitName qi
, ("limit" .=) <$> limit qi
, ("futureLimit" .=) <$> futureLimit qi
, ("rolloutStatus" .=) <$> rolloutStatus qi
]
data Help = Help
{ links :: Maybe [Link]
}
deriving (Show, Eq)
instance FromJSON Help where
parseJSON = withObject "Help" $ \o -> Help <$> o .:? "links"
instance ToJSON Help where
toJSON h = object $ catMaybes [("links" .=) <$> links h]
data Link = Link
{ description :: Maybe String
, url :: Maybe String
}
deriving (Show, Eq)
instance FromJSON Link where
parseJSON = withObject "Link" $ \o ->
Link
<$> o .:? "description"
<*> o .:? "url"
instance ToJSON Link where
toJSON Link {..} =
object $
catMaybes
[ ("description" .=) <$> description
, ("url" .=) <$> url
]
data LocalizedMessage = LocalizedMessage
{ locale :: Maybe String
, message :: Maybe String
}
deriving (Show, Eq)
instance FromJSON LocalizedMessage where
parseJSON = withObject "LocalizedMessage" $ \o ->
LocalizedMessage
<$> o .:? "locale"
<*> o .:? "message"
instance ToJSON LocalizedMessage where
toJSON LocalizedMessage {..} =
object $
catMaybes
[ ("locale" .=) <$> locale
, ("message" .=) <$> message
]
data InstanceStartResponse = InstanceStartResponse
{ kind :: Maybe Text
, id_ :: Maybe Text
, creationTimestamp :: Maybe Text
, name :: Maybe Text
, zone :: Maybe Text
, clientOperationId :: Maybe Text
, operationType :: Maybe Text
, targetLink :: Maybe Text
, targetId :: Maybe Text
, status :: Maybe Text
, statusMessage :: Maybe Text
, user :: Maybe Text
}
deriving (Show, Eq)
instance FromJSON InstanceStartResponse where
parseJSON = withObject "InstanceStartResponse" $ \o ->
InstanceStartResponse
<$> o .:? "kind"
<*> o .:? "id"
<*> o .:? "creationTimestamp"
<*> o .:? "name"
<*> o .:? "zone"
<*> o .:? "clientOperationId"
<*> o .:? "operationType"
<*> o .:? "targetLink"
<*> o .:? "targetId"
<*> o .:? "status"
<*> o .:? "statusMessage"
<*> o .:? "user"
instance ToJSON InstanceStartResponse where
toJSON InstanceStartResponse {..} =
object $
catMaybes
[ ("kind" .=) <$> kind
, ("id" .=) <$> id_
, ("creationTimestamp" .=) <$> creationTimestamp
, ("name" .=) <$> name
, ("zone" .=) <$> zone
, ("clientOperationId" .=) <$> clientOperationId
, ("operationType" .=) <$> operationType
, ("targetLink" .=) <$> targetLink
, ("targetId" .=) <$> targetId
, ("status" .=) <$> status
, ("statusMessage" .=) <$> statusMessage
, ("user" .=) <$> user
]
data InsertInstanceOps = InsertInstanceOps
{ name :: String
, machineType :: String
, disks :: Maybe [Disk]
, networkInterfaces :: Maybe [NetworkInterface]
}
deriving (Show, Eq)
data NetworkInterface = NetworkInterface
{ network :: String
}
deriving (Show, Eq)
data InsertInstanceQuery = InsertInstanceQuery
{ requestId :: Maybe String
, sourceInstanceTemplate :: Maybe String
}
deriving (Show, Eq)
instance ToJSON InsertInstanceOps where
toJSON InsertInstanceOps {..} =
object
[ "name" .= name
, "machineType" .= machineType
, "disks" .= disks
, "networkInterfaces" .= networkInterfaces
]
instance ToJSON NetworkInterface where
toJSON ni =
object
[ "network" .= network ni
]
-- | Represents a disk attached to a Compute Engine instance
data Disk = Disk
{ autoDelete :: Bool
-- ^ Whether disk should be auto-deleted when instance is deleted
, boot :: Bool
-- ^ Whether this is a boot disk
, deviceName :: String
-- ^ Unique device name visible to the guest OS
, initializeParams :: Maybe InitializeParams
-- ^ Initialization parameters for the disk
, mode :: String
-- ^ Access mode (READ_WRITE or READ_ONLY)
, type_ :: String
-- ^ Disk type (PERSISTENT or SCRATCH)
}
deriving (Show, Eq)
-- | Disk initialization parameters for new disk creation
data InitializeParams = InitializeParams
{ diskSizeGb :: String
-- ^ Size of the disk in GB (passed as String to match API format)
, diskType :: String
-- ^ Full URL of disk type resource
, labels :: Map.Map String String
-- ^ Labels to apply to the disk
, sourceImage :: String
-- ^ Source image used to create the disk
}
deriving (Show, Eq)
instance ToJSON Disk where
toJSON Disk{..} = object
[ "autoDelete" .= autoDelete
, "boot" .= boot
, "deviceName" .= deviceName
, "initializeParams" .= initializeParams
, "mode" .= mode
, "type" .= type_ -- Map type_ to "type" in JSON
]
instance FromJSON Disk where
parseJSON = withObject "Disk" $ \v -> Disk
<$> v .: "autoDelete"
<*> v .: "boot"
<*> v .: "deviceName"
<*> v .:? "initializeParams"
<*> v .: "mode"
<*> v .: "type" -- Parse "type" field into type_
instance ToJSON InitializeParams where
toJSON InitializeParams{..} = object
[ "diskSizeGb" .= diskSizeGb
, "diskType" .= diskType
, "labels" .= labels
, "sourceImage" .= sourceImage
]
instance FromJSON InitializeParams where
parseJSON = withObject "InitializeParams" $ \v -> InitializeParams
<$> v .: "diskSizeGb"
<*> v .: "diskType"
<*> v .: "labels"
<*> v .: "sourceImage"
-- ** Instance Operation Functions**
-- | List instances in a given zone
--
-- @
-- listInstances
-- :: String -- ^ GCP Project ID
-- -> String -- ^ Zone name
-- -> Maybe ListInstancesQuery -- ^ Optional query parameters
-- -> IO (Either String InstanceList)
-- @
--
-- Returns either an error message or an 'InstanceList' containing
-- the collection of instances
listInstances ::
String -> String -> Maybe ListInstancesQuery -> IO (Either String InstanceList)
listInstances projectId zone_ mbQuery = do
let queryParams = maybe [] toQueryList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = GET
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath = Just ("/projects/" <> projectId <> "/zones/" <> zone_ <> "/instances")
}
where
toQueryList :: ListInstancesQuery -> [(BS.ByteString, Maybe BS.ByteString)]
toQueryList q =
catMaybes
[ ("filter",) . Just . BS8.pack <$> filter_ q
, ("maxResults",) . Just . BS8.pack . show <$> maxResults q
, ("orderBy",) . Just . BS8.pack <$> orderBy q
, ("pageToken",) . Just . BS8.pack <$> pageToken q
, ("returnPartialSuccess",) . Just . BS8.pack . show <$> returnPartialSuccess q
]
deleteInstance ::
String -> String -> String -> Maybe RequestIdQuery -> IO (Either String InstanceDeleteResp)
deleteInstance project zone_ name_ mbQuery = do
let queryParams = maybe [] toQueryList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = DELETE
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath = Just ("/projects/" <> project <> "/zones/" <> zone_ <> "/instances/" <> name_)
}
where
toQueryList :: RequestIdQuery -> [(BS.ByteString, Maybe BS.ByteString)]
toQueryList RequestIdQuery {..} =
catMaybes
[("requestId",) . Just . BS8.pack <$> requestId]
startInstance ::
String ->
String ->
String ->
Maybe RequestIdQuery ->
IO (Either String InstanceStartResponse)
startInstance projectId zone_ instanceName mbQuery = do
let queryParams = maybe [] toQueryList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = POST
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath =
Just ("/projects/" <> projectId <> "/zones/" <> zone_ <> "/instances/" <> instanceName <> "/start")
}
where
toQueryList :: RequestIdQuery -> [(BS.ByteString, Maybe BS.ByteString)]
toQueryList RequestIdQuery {..} =
catMaybes
[("requestId",) . Just . BS8.pack <$> requestId]
stopInstance ::
String ->
String ->
String ->
Maybe RequestIdQuery ->
IO (Either String InstanceStartResponse)
stopInstance projectId zone_ instanceName mbQuery = do
let queryParams = maybe [] toQueryList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = POST
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath =
Just ("/projects/" <> projectId <> "/zones/" <> zone_ <> "/instances/" <> instanceName <> "/stop")
}
where
toQueryList :: RequestIdQuery -> [(BS.ByteString, Maybe BS.ByteString)]
toQueryList RequestIdQuery {..} =
catMaybes
[("requestId",) . Just . BS8.pack <$> requestId]
insertInstance ::
String ->
String ->
InsertInstanceOps ->
Maybe InsertInstanceQuery ->
IO (Either String InstanceStartResponse)
insertInstance projectId zone_ insertOps mbQuery = do
let queryParams = maybe [] toQueryList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = POST
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Just $ encode insertOps
, mbReqHeaders = Nothing
, mbReqPath = Just $ "/projects/" <> projectId <> "/zones/" <> zone_ <> "/instances"
}
where
toQueryList :: InsertInstanceQuery -> [(BS.ByteString, Maybe BS.ByteString)]
toQueryList InsertInstanceQuery {..} =
catMaybes
[ ("requestId",) . Just . BS8.pack <$> requestId
, ("sourceInstanceTemplate",) . Just . BS8.pack <$> sourceInstanceTemplate
]
-- ** Default Query Parameter Constructors**
-- All fields set to 'Nothing', which the API interprets as:
-- * No filtering
-- * Default page size
-- * No sorting
-- * Return full results
defaultListInstancesQuery :: ListInstancesQuery
defaultListInstancesQuery =
ListInstancesQuery
{ filter_ = Nothing
, maxResults = Nothing
, orderBy = Nothing
, pageToken = Nothing
, returnPartialSuccess = Nothing
}
defaultRequestIdQuery :: String -> RequestIdQuery
defaultRequestIdQuery rid = RequestIdQuery {requestId = Just rid}
-- | Create a default instance configuration
--
-- Creates a basic instance configuration with:
-- * 10GB persistent boot disk
-- * Default Debian image
-- * Network interface on default VPC
defaultInsertInstanceOps :: String -> String -> String -> String -> InsertInstanceOps
defaultInsertInstanceOps projectId zone instanceName machineType =
InsertInstanceOps
{ name = instanceName
, machineType = "zones/" ++ zone ++ "/machineTypes/" ++ machineType
, disks = Just [defaultDisk]
, networkInterfaces = Just [defaultNetworkInterface]
}
where
defaultDisk = Disk
{ autoDelete = True
, boot = True
, deviceName = instanceName ++ "-boot" -- Dynamic, based on instanceName
, initializeParams = Just defaultInitializeParams
, mode = "READ_WRITE"
, type_ = "PERSISTENT"
}
defaultInitializeParams = InitializeParams
{ diskSizeGb = "10"
, diskType = "projects/" ++ projectId ++ "/zones/" ++ zone ++ "/diskTypes/pd-standard"
, labels = Map.empty
, sourceImage = "projects/debian-cloud/global/images/debian-12-bookworm-v20250311"
}
defaultNetworkInterface = NetworkInterface
{ network = "projects/" ++ projectId ++ "/global/networks/default"
}