google-cloud-compute-1.1.0.0: src/Google/Cloud/Compute/Disk.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{- |
Module : Google.Cloud.Compute.Disk
Copyright : (c) 2025
License : MIT
Maintainer :
Stability : experimental
This module provides a Haskell client for Google Cloud Compute Engine Disk operations.
-}
module Google.Cloud.Compute.Disk
( listDisks
, insertDisk
, DiskResponse (..)
, InsertDiskOps (..)
, InsertDiskResponse
, Operation (..)
, deleteDisk
, createDiskSnapshot
, CreateSnapshotResp
, CreateSnapshotOps (..)
, Warning (..)
, WarningData (..)
, defaultCreateSnapshotOps
, defaultInsertOps
) where
import Data.Aeson
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.Map (Map)
import Google.Cloud.Common.Core
import Google.Cloud.Compute.Common
import Network.HTTP.Simple
-- | Represents a warning object returned by Google Cloud API operations
data Warning = Warning
{ code :: String
-- ^ Warning type identifier
, message :: String
-- ^ Human-readable warning message
, data_ :: Maybe [WarningData]
-- ^ Optional metadata about the warning
}
deriving (Show, Eq)
-- | Key-value pair providing additional context for warnings
data WarningData = WarningData
{ key :: String
-- ^ Metadata key describing the warning aspect
, value :: String
-- ^ Metadata value associated with the key
}
deriving (Show, Eq)
-- | Response format for disk listing operations
data DiskResponse = DiskResponse
{ kind :: Maybe String
-- ^ Type identifier ("compute#diskList")
, id :: Maybe String
-- ^ Unique identifier for the resource
, items :: Maybe [DiskItem]
-- ^ List of disks in the project/zone
, nextPageToken :: Maybe String
-- ^ Pagination token for next page
, selfLink :: Maybe String
-- ^ Server-defined URL for the resource
, warning :: Maybe Warning
-- ^ Any warnings about the result
}
deriving (Show, Eq)
-- | Represents a Google Cloud Persistent Disk
data DiskItem = DiskItem
{ kind :: Maybe String
-- ^ Type identifier ("compute#disk")
, id :: Maybe String
-- ^ Unique numeric ID
, creationTimestamp :: Maybe String
-- ^ ISO 8601 creation timestamp
, name :: Maybe String
-- ^ User-provided disk name
, description :: Maybe String
-- ^ Optional disk description
, sizeGb :: Maybe String
-- ^ Disk size in gigabytes
, zone :: Maybe String
-- ^ Zone where the disk resides
, status :: Maybe String
-- ^ Current status (READY, CREATING, etc.)
, sourceSnapshot :: Maybe String
-- ^ Source snapshot URL if applicable
, sourceSnapshotId :: Maybe String
-- ^ Unique ID of source snapshot
, sourceImage :: Maybe String
-- ^ Source image URL if applicable
, sourceImageId :: Maybe String
-- ^ Unique ID of source image
, type_ :: Maybe String
-- ^ Disk type (e.g., "pd-standard")
, labels :: Maybe (Map String String)
-- ^ User-defined labels
, labelFingerprint :: Maybe String
-- ^ Fingerprint for label operations
, licenses :: Maybe [String]
-- ^ License resource URLs
, users :: Maybe [String]
-- ^ Instances currently using the disk
, guestOsFeatures :: Maybe [GuestOsFeature]
-- ^ OS-specific features
, diskEncryptionKey :: Maybe EncryptionKey
-- ^ Disk encryption details
, sourceDisk :: Maybe String
-- ^ Source disk URL for regional disks
, sourceDiskId :: Maybe String
-- ^ Unique ID of source disk
, physicalBlockSizeBytes :: Maybe String
-- ^ Physical block size in bytes
, provisionedIops :: Maybe String
-- ^ Provisioned IOPS for SSD
, resourcePolicies :: Maybe [String]
-- ^ Resource policy URLs
, selfLink :: Maybe String
-- ^ Server-defined URL for the disk
}
deriving (Show, Eq)
-- | Guest OS features enabled on the disk
newtype GuestOsFeature = GuestOsFeature
{ type_ :: String
-- ^ Feature type (e.g., "UEFI_COMPATIBLE")
}
deriving (Show, Eq)
-- | Disk encryption key information
data EncryptionKey = EncryptionKey
{ rawKey :: Maybe String
-- ^ Raw encryption key (base64)
, rsaEncryptedKey :: Maybe String
-- ^ RSA-wrapped encryption key
, sha256 :: Maybe String
-- ^ RFC 4648 base64 SHA-256 hash
}
deriving (Show, Eq)
-- | Long-running operation resource
data Operation = Operation
{ id :: Maybe String
-- ^ Unique operation ID
, name :: Maybe String
-- ^ Operation name
, zone :: Maybe String
-- ^ Zone where operation is executed
, operationType :: Maybe String
-- ^ Operation type (insert, delete, etc.)
, targetLink :: Maybe String
-- ^ URL of resource being modified
, status :: Maybe String
-- ^ Current status (DONE, RUNNING, etc.)
, progress :: Maybe Int
-- ^ Operation progress (0-100)
, insertTime :: Maybe String
-- ^ ISO 8601 creation timestamp
, startTime :: Maybe String
-- ^ ISO 8601 start timestamp
, endTime :: Maybe String
-- ^ ISO 8601 completion timestamp
, error :: Maybe OperationError
-- ^ Error details if failed
, warnings :: Maybe [Warning]
-- ^ Warnings during operation
, selfLink :: Maybe String
-- ^ Server-defined URL for the operation
}
deriving (Show, Eq)
-- | Error information from failed operations
newtype OperationError = OperationError
{ errors :: [ErrorDetail]
-- ^ List of error details
}
deriving (Show, Eq)
-- | Detailed error information
data ErrorDetail = ErrorDetail
{ code :: String
-- ^ Error type identifier
, message :: String
-- ^ Human-readable error message
, location :: Maybe String
-- ^ Location in request that triggered error
}
deriving (Show, Eq)
-- | Disk creation options
data InsertDiskOps = InsertDiskOps
{ name :: String
-- ^ Required disk name
, sizeGb :: String
-- ^ Required size in gigabytes
, description :: Maybe String
-- ^ Optional description
, type_ :: Maybe String
-- ^ Disk type (default: pd-standard)
, sourceImage :: Maybe String
-- ^ Source image URL for disk creation
, sourceSnapshot :: Maybe String
-- ^ Source snapshot URL for disk creation
, labels :: Maybe (Map String String)
-- ^ Initial labels
, guestOsFeatures :: Maybe [GuestOsFeature]
-- ^ OS features to enable
, diskEncryptionKey :: Maybe EncryptionKey
-- ^ Encryption configuration
}
deriving (Show, Eq)
-- | Alias for operation response from disk insertion
type InsertDiskResponse = Operation
-- | Snapshot creation options
data CreateSnapshotOps = CreateSnapshotOps
{ name :: String
-- ^ Required snapshot name
, description :: Maybe String
-- ^ Optional description
, labels :: Maybe (Map String String)
-- ^ Labels for the snapshot
, storageLocations :: Maybe [String]
-- ^ Preferred storage locations
, snapshotEncryptionKey :: Maybe EncryptionKey
-- ^ Snapshot encryption
}
deriving (Show, Eq)
-- | Alias for operation response from snapshot creation
type CreateSnapshotResp = Operation
data ListDisksQuery = ListDisksQuery
{ filter_ :: Maybe String
, maxResults :: Maybe Int
, pageToken :: Maybe String
, orderBy :: Maybe String
}
deriving (Show, Eq)
-- | Query parameters for insertDisk
data InsertDiskQuery = InsertDiskQuery
{ sourceImage :: Maybe String
, sourceImageEncryptionKey :: Maybe EncryptionKey
}
deriving (Show, Eq)
-- | Query parameters for deleteDisk
newtype DeleteDiskQuery = DeleteDiskQuery
{ force :: Maybe Bool
}
deriving (Show, Eq)
-- | Query parameters for createDiskSnapshot
newtype CreateSnapshotQuery = CreateSnapshotQuery
{ guestFlush :: Maybe Bool
}
deriving (Show, Eq)
-- JSON Instances
instance FromJSON WarningData where
parseJSON = withObject "WarningData" $ \v ->
WarningData
<$> v .: "key"
<*> v .: "value"
instance ToJSON WarningData where
toJSON (WarningData key value) =
object
[ "key" .= key
, "value" .= value
]
instance FromJSON Warning where
parseJSON = withObject "Warning" $ \v ->
Warning
<$> v .: "code"
<*> v .: "message"
<*> v .:? "data"
instance ToJSON Warning where
toJSON (Warning code message data_) =
object
[ "code" .= code
, "message" .= message
, "data" .= data_
]
instance FromJSON GuestOsFeature where
parseJSON = withObject "GuestOsFeature" $ \v ->
GuestOsFeature
<$> v .: "type"
instance ToJSON GuestOsFeature where
toJSON (GuestOsFeature type_) =
object
[ "type" .= type_
]
instance FromJSON EncryptionKey where
parseJSON = withObject "EncryptionKey" $ \v ->
EncryptionKey
<$> v .:? "rawKey"
<*> v .:? "rsaEncryptedKey"
<*> v .:? "sha256"
instance ToJSON EncryptionKey where
toJSON (EncryptionKey rawKey rsaEncryptedKey sha256) =
object
[ "rawKey" .= rawKey
, "rsaEncryptedKey" .= rsaEncryptedKey
, "sha256" .= sha256
]
instance FromJSON DiskItem where
parseJSON = withObject "DiskItem" $ \v ->
DiskItem
<$> v .:? "kind"
<*> v .:? "id"
<*> v .:? "creationTimestamp"
<*> v .:? "name"
<*> v .:? "description"
<*> v .:? "sizeGb"
<*> v .:? "zone"
<*> v .:? "status"
<*> v .:? "sourceSnapshot"
<*> v .:? "sourceSnapshotId"
<*> v .:? "sourceImage"
<*> v .:? "sourceImageId"
<*> v .:? "type"
<*> v .:? "labels"
<*> v .:? "labelFingerprint"
<*> v .:? "licenses"
<*> v .:? "users"
<*> v .:? "guestOsFeatures"
<*> v .:? "diskEncryptionKey"
<*> v .:? "sourceDisk"
<*> v .:? "sourceDiskId"
<*> v .:? "physicalBlockSizeBytes"
<*> v .:? "provisionedIops"
<*> v .:? "resourcePolicies"
<*> v .:? "selfLink"
instance ToJSON DiskItem where
toJSON
( DiskItem
kind
id_
creationTimestamp
name
description
sizeGb
zone
status
sourceSnapshot
sourceSnapshotId
sourceImage
sourceImageId
type_
labels
labelFingerprint
licenses
users
guestOsFeatures
diskEncryptionKey
sourceDisk
sourceDiskId
physicalBlockSizeBytes
provisionedIops
resourcePolicies
selfLink
) =
object
[ "kind" .= kind
, "id" .= id_
, "creationTimestamp" .= creationTimestamp
, "name" .= name
, "description" .= description
, "sizeGb" .= sizeGb
, "zone" .= zone
, "status" .= status
, "sourceSnapshot" .= sourceSnapshot
, "sourceSnapshotId" .= sourceSnapshotId
, "sourceImage" .= sourceImage
, "sourceImageId" .= sourceImageId
, "type" .= type_
, "labels" .= labels
, "labelFingerprint" .= labelFingerprint
, "licenses" .= licenses
, "users" .= users
, "guestOsFeatures" .= guestOsFeatures
, "diskEncryptionKey" .= diskEncryptionKey
, "sourceDisk" .= sourceDisk
, "sourceDiskId" .= sourceDiskId
, "physicalBlockSizeBytes" .= physicalBlockSizeBytes
, "provisionedIops" .= provisionedIops
, "resourcePolicies" .= resourcePolicies
, "selfLink" .= selfLink
]
instance FromJSON DiskResponse where
parseJSON = withObject "DiskResponse" $ \v ->
DiskResponse
<$> v .:? "kind"
<*> v .:? "id"
<*> v .:? "items"
<*> v .:? "nextPageToken"
<*> v .:? "selfLink"
<*> v .:? "warning"
instance ToJSON DiskResponse where
toJSON (DiskResponse kind id_ items nextPageToken selfLink warning) =
object
[ "kind" .= kind
, "id" .= id_
, "items" .= items
, "nextPageToken" .= nextPageToken
, "selfLink" .= selfLink
, "warning" .= warning
]
instance FromJSON ErrorDetail where
parseJSON = withObject "ErrorDetail" $ \v ->
ErrorDetail
<$> v .: "code"
<*> v .: "message"
<*> v .:? "location"
instance ToJSON ErrorDetail where
toJSON (ErrorDetail code message location) =
object
[ "code" .= code
, "message" .= message
, "location" .= location
]
instance FromJSON OperationError where
parseJSON = withObject "OperationError" $ \v ->
OperationError
<$> v .: "errors"
instance ToJSON OperationError where
toJSON (OperationError errors) =
object
[ "errors" .= errors
]
instance FromJSON Operation where
parseJSON = withObject "Operation" $ \v ->
Operation
<$> v .:? "id"
<*> v .:? "name"
<*> v .:? "zone"
<*> v .:? "operationType"
<*> v .:? "targetLink"
<*> v .:? "status"
<*> v .:? "progress"
<*> v .:? "insertTime"
<*> v .:? "startTime"
<*> v .:? "endTime"
<*> v .:? "error"
<*> v .:? "warnings"
<*> v .:? "selfLink"
instance ToJSON Operation where
toJSON
( Operation
id_
name
zone
operationType
targetLink
status
progress
insertTime
startTime
endTime
error_
warnings
selfLink
) =
object
[ "id" .= id_
, "name" .= name
, "zone" .= zone
, "operationType" .= operationType
, "targetLink" .= targetLink
, "status" .= status
, "progress" .= progress
, "insertTime" .= insertTime
, "startTime" .= startTime
, "endTime" .= endTime
, "error" .= error_
, "warnings" .= warnings
, "selfLink" .= selfLink
]
instance ToJSON InsertDiskOps where
toJSON
( InsertDiskOps
name
sizeGb
description
type_
sourceImage
sourceSnapshot
labels
guestOsFeatures
diskEncryptionKey
) =
object
[ "name" .= name
, "sizeGb" .= sizeGb
, "description" .= description
, "type" .= type_
, "sourceImage" .= sourceImage
, "sourceSnapshot" .= sourceSnapshot
, "labels" .= labels
, "guestOsFeatures" .= guestOsFeatures
, "diskEncryptionKey" .= diskEncryptionKey
]
instance ToJSON CreateSnapshotOps where
toJSON (CreateSnapshotOps name description labels storageLocations snapshotEncryptionKey) =
object
[ "name" .= name
, "description" .= description
, "labels" .= labels
, "storageLocations" .= storageLocations
, "snapshotEncryptionKey" .= snapshotEncryptionKey
]
-- Functions
{- | List disks in a zone
Example:
> listDisks "my-project" "us-central1-a" Nothing
-}
listDisks ::
-- | GCP Project ID
String ->
-- | Zone name
String ->
-- | Optional query parameters
Maybe ListDisksQuery ->
-- | List result or error}
IO (Either String DiskResponse)
listDisks project zone_ mbQuery = do
let queryParams = maybe [] queryToList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = GET
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath = Just $ "/projects/" <> project <> "/zones/" <> zone_ <> "/disks"
}
where
queryToList :: ListDisksQuery -> Query
queryToList ListDisksQuery {..} =
[ maybe mempty (\x -> ("filter", Just $ BS.pack x)) filter_
, maybe mempty (\x -> ("maxResults", Just . BS.pack $ show x)) maxResults
, maybe mempty (\x -> ("pageToken", Just $ BS.pack x)) pageToken
, maybe mempty (\x -> ("orderBy", Just $ BS.pack x)) orderBy
]
{- | Create a new persistent disk
Example:
> insertDisk "my-project" "us-central1-a" (defaultInsertOps "disk1" "100") Nothing
-}
insertDisk ::
-- | GCP Project ID
String ->
-- | Zone name
String ->
-- | Disk configuration
InsertDiskOps ->
-- | Optional query parameters
Maybe InsertDiskQuery ->
-- | Operation or error}
IO (Either String InsertDiskResponse)
insertDisk project zone_ insertDiskOps mbQuery = do
let queryParams = maybe [] queryToList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = POST
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Just $ encode insertDiskOps
, mbReqHeaders = Nothing
, mbReqPath = Just $ "/projects/" <> project <> "/zones/" <> zone_ <> "/disks"
}
where
queryToList :: InsertDiskQuery -> Query
queryToList InsertDiskQuery {sourceImage, sourceImageEncryptionKey} =
[ maybe mempty (\x -> ("sourceImage", Just $ BS.pack x)) sourceImage
, maybe
mempty
(\x -> ("sourceImageEncryptionKey", Just . BSL.toStrict $ encode x))
sourceImageEncryptionKey
]
-- | Deletes a disk with optional query parameters
{- | Delete a persistent disk
Example:
> deleteDisk "my-project" "us-central1-a" "old-disk" (Just DeleteDiskQuery { force = True })
-}
deleteDisk ::
-- | GCP Project ID
String ->
-- | Zone name
String ->
-- | Disk name to delete
String ->
-- | Optional force deletion
Maybe DeleteDiskQuery ->
-- | Operation or error
IO (Either String Operation)
deleteDisk project zone_ diskName mbQuery = do
let queryParams = maybe [] queryToList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = DELETE
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Nothing
, mbReqHeaders = Nothing
, mbReqPath = Just $ "/projects/" <> project <> "/zones/" <> zone_ <> "/disks/" <> diskName
}
where
queryToList :: DeleteDiskQuery -> Query
queryToList q =
[ maybe mempty (\x -> ("force", Just . BS.pack $ show x)) (force q)
]
{- | Creates a snapshot of a disk with optional query parameters
| Create disk snapshot
Example:
> createDiskSnapshot "my-project" "us-central1-a" "source-disk"
> (defaultCreateSnapshotOps "daily-backup") Nothing
-}
createDiskSnapshot ::
-- | GCP Project ID
String ->
-- | Zone name
String ->
-- | Source disk name
String ->
-- | Snapshot configuration
CreateSnapshotOps ->
-- | Optional guest flush
Maybe CreateSnapshotQuery ->
-- | Operation or error
IO (Either String CreateSnapshotResp)
createDiskSnapshot project zone_ diskName createSnapshotOps mbQuery = do
let queryParams = maybe [] queryToList mbQuery
doRequestJSON
RequestOptions
{ reqMethod = POST
, reqUrl = googleComputeUrl
, mbQueryParams = Just queryParams
, mbReqBody = Just $ encode createSnapshotOps
, mbReqHeaders = Nothing
, mbReqPath =
Just $ "/projects/" <> project <> "/zones/" <> zone_ <> "/disks/" <> diskName <> "/createSnapshot"
}
where
queryToList :: CreateSnapshotQuery -> Query
queryToList q =
[ maybe mempty (\x -> ("guestFlush", Just . BS.pack $ show x)) (guestFlush q)
]
{- | Default disk creation options
Creates minimal valid configuration with required name and size.
Other fields can be added using record update syntax:
> defaultInsertOps "new-disk" "50" { type_ = Just "pd-ssd" }
-}
defaultInsertOps :: String -> String -> InsertDiskOps
defaultInsertOps diskName diskSizeGb =
InsertDiskOps
{ name = diskName
, sizeGb = diskSizeGb
, description = Nothing
, type_ = Nothing
, sourceImage = Nothing
, sourceSnapshot = Nothing
, labels = Nothing
, guestOsFeatures = Nothing
, diskEncryptionKey = Nothing
}
{- | Default snapshot creation options
Creates minimal valid configuration with required snapshot name.
Additional fields can be added using record update syntax:
> defaultCreateSnapshotOps "backup" { description = Just "Daily backup" }
-}
defaultCreateSnapshotOps :: String -> CreateSnapshotOps
defaultCreateSnapshotOps snapshotName =
CreateSnapshotOps
{ name = snapshotName
, description = Nothing
, labels = Nothing
, storageLocations = Nothing
, snapshotEncryptionKey = Nothing
}