google-cloud-compute (empty) → 0.1.0.0
raw patch · 11 files changed
+2880/−0 lines, 11 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, containers, google-cloud-common, google-cloud-compute, hspec, http-conduit, text
Files
- CHANGELOG.md +11/−0
- LICENSE +21/−0
- README.md +5/−0
- Setup.hs +2/−0
- google-cloud-compute.cabal +68/−0
- src/Google/Cloud/Compute/Common.hs +4/−0
- src/Google/Cloud/Compute/Disk.hs +627/−0
- src/Google/Cloud/Compute/Firewall.hs +585/−0
- src/Google/Cloud/Compute/Instance.hs +1145/−0
- src/Google/Cloud/Compute/Network.hs +241/−0
- test/Spec.hs +171/−0
+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog for `google-cloud-compute`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - YYYY-MM-DD
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2025 Tushar Adhatrao++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,5 @@+# google-cloud-compute++Haskell idiomatic client for [Google Cloud Platform](https://cloud.google.com/) Compute service.++Full docs are available at https://github.com/tusharad/google-cloud-haskell
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ google-cloud-compute.cabal view
@@ -0,0 +1,68 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.37.0.+--+-- see: https://github.com/sol/hpack++name: google-cloud-compute+version: 0.1.0.0+synopsis: GCP Client for Haskell+description: GCP Compute client for Haskell.+category: Web+homepage: https://github.com/tusharad/google-cloud-haskell#readme+bug-reports: https://github.com/tusharad/google-cloud-haskell/issues+author: tushar+maintainer: tusharadhatrao@gmail.com+copyright: 2025 tushar+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ CHANGELOG.md++source-repository head+ type: git+ location: https://github.com/tusharad/google-cloud-haskell++library+ exposed-modules:+ Google.Cloud.Compute.Common+ Google.Cloud.Compute.Disk+ Google.Cloud.Compute.Firewall+ Google.Cloud.Compute.Instance+ Google.Cloud.Compute.Network+ other-modules:+ Paths_google_cloud_compute+ hs-source-dirs:+ src+ ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+ build-depends:+ aeson <3+ , base >=4.7 && <5+ , bytestring >=0.9.1.4+ , containers >=0.6 && <1+ , google-cloud-common+ , http-conduit >=2.2 && <2.4+ , text >=1.2 && <3+ default-language: Haskell2010++test-suite google-cloud-compute-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Paths_google_cloud_compute+ hs-source-dirs:+ test+ ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ aeson <3+ , base >=4.7 && <5+ , bytestring >=0.9.1.4+ , containers >=0.6 && <1+ , google-cloud-common+ , google-cloud-compute+ , hspec >=1.3+ , http-conduit >=2.2 && <2.4+ , text >=1.2 && <3+ default-language: Haskell2010
+ src/Google/Cloud/Compute/Common.hs view
@@ -0,0 +1,4 @@+module Google.Cloud.Compute.Common (googleComputeUrl) where++googleComputeUrl :: String+googleComputeUrl = "https://compute.googleapis.com/compute/v1"
+ src/Google/Cloud/Compute/Disk.hs view
@@ -0,0 +1,627 @@+{-|+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.+-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++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 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+data 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+data 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+data DeleteDiskQuery = DeleteDiskQuery+ { force :: Maybe Bool+ }+ deriving (Show, Eq)++-- | Query parameters for createDiskSnapshot+data 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 :: String -- ^ GCP Project ID+ -> String -- ^ Zone name+ -> Maybe ListDisksQuery -- ^ Optional query parameters+ -> IO (Either String DiskResponse) -- ^ List result or error}+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 :: String -- ^ GCP Project ID+ -> String -- ^ Zone name+ -> InsertDiskOps -- ^ Disk configuration+ -> Maybe InsertDiskQuery -- ^ Optional query parameters+ -> IO (Either String InsertDiskResponse) -- ^ Operation or error}+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 . BS.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 :: String -- ^ GCP Project ID+ -> String -- ^ Zone name+ -> String -- ^ Disk name to delete+ -> Maybe DeleteDiskQuery -- ^ Optional force deletion+ -> IO (Either String Operation) -- ^ Operation or error+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 :: String -- ^ GCP Project ID+ -> String -- ^ Zone name+ -> String -- ^ Source disk name+ -> CreateSnapshotOps -- ^ Snapshot configuration+ -> Maybe CreateSnapshotQuery -- ^ Optional guest flush+ -> IO (Either String CreateSnapshotResp) -- ^ Operation or error+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+ }
+ src/Google/Cloud/Compute/Firewall.hs view
@@ -0,0 +1,585 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}++{- |+Module : Google.Cloud.Compute.Firewall+Copyright : (c) 2025 +License : MIT+Maintainer : +Stability : experimental++This module provides a Haskell client for Google Cloud Compute Engine Firewall operations.+-}+module Google.Cloud.Compute.Firewall+ ( listFirewalls+ , FirewallList (..)+ , FirewallMeta (..)+ , AllowedFirewall (..)+ , createFirewall+ , CreateFirewallOps (..)+ , CreateFirewallResp+ , Operation (..)+ , Warning (..)+ , WarningData (..)+ , LogConfig (..)+ , DeniedFirewall (..)+ , defaultCreateFirewallOps+ ) where++import Data.Aeson+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BS8+import Data.Maybe+import Google.Cloud.Common.Core+import Google.Cloud.Compute.Common++-- | Represents a warning object in API responses+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 firewall listing operations+data FirewallList = FirewallList+ { kind :: Maybe String+ -- ^ Type identifier ("compute#firewallList")+ , id :: Maybe String+ -- ^ Unique identifier for the resource+ , items :: Maybe [FirewallMeta]+ -- ^ List of firewall rules+ , 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 Firewall Rule+data FirewallMeta = FirewallMeta+ { kind :: Maybe String+ -- ^ Type identifier ("compute#firewall")+ , id :: Maybe String+ -- ^ Unique numeric ID+ , creationTimestamp :: Maybe String+ -- ^ ISO 8601 creation timestamp+ , name :: Maybe String+ -- ^ User-provided firewall name+ , description :: Maybe String+ -- ^ Optional rule description+ , network :: Maybe String+ -- ^ Network URL the rule applies to+ , priority :: Maybe Int+ -- ^ Rule priority (0-65535, lower=higher)+ , sourceRanges :: Maybe [String]+ -- ^ Allowed source IP ranges (CIDR)+ , sourceTags :: Maybe [String]+ -- ^ Source instance tags+ , targetTags :: Maybe [String]+ -- ^ Target instance tags+ , allowed :: Maybe [AllowedFirewall]+ -- ^ Allow rules+ , denied :: Maybe [DeniedFirewall]+ -- ^ Deny rules+ , direction :: Maybe String+ -- ^ Traffic direction (INGRESS/EGRESS)+ , logConfig :: Maybe LogConfig+ -- ^ Logging configuration+ , disabled :: Maybe Bool+ -- ^ Whether rule is disabled+ , selfLink :: Maybe String+ -- ^ Server-defined URL for the rule+ }+ deriving (Show, Eq)++-- | Allow rule specification+data AllowedFirewall = AllowedFirewall+ { iPProtocol :: String+ -- ^ Protocol name (e.g., "tcp", "udp", "icmp")+ , ports :: Maybe [String]+ -- ^ Optional port numbers/ranges (e.g., "80", "8000-8080")+ }+ deriving (Show, Eq)++-- | Deny rule specification+data DeniedFirewall = DeniedFirewall+ { iPProtocol :: String+ -- ^ Protocol name (e.g., "tcp", "udp")+ , ports :: Maybe [String]+ -- ^ Optional port numbers/ranges to block+ }+ deriving (Show, Eq)++-- | Firewall rule logging configuration+data LogConfig = LogConfig+ { enable :: Bool+ -- ^ Whether to enable logging+ , metadata :: Maybe String+ -- ^ Log metadata level (EXCLUDE_ALL_METADATA or INCLUDE_ALL_METADATA)+ }+ deriving (Show, Eq)++-- | Long-running operation resource+data Operation = Operation+ { id :: Maybe String+ -- ^ Unique operation ID+ , name :: Maybe String+ -- ^ Operation name+ , 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+data 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)++-- | Firewall creation options+data CreateFirewallOps = CreateFirewallOps+ { name :: String+ -- ^ Required firewall rule name+ , description :: Maybe String+ -- ^ Optional description+ , network :: Maybe String+ -- ^ Network URL (default: "global/networks/default")+ , priority :: Maybe Int+ -- ^ Priority (default: 1000)+ , sourceRanges :: Maybe [String]+ -- ^ Source IP ranges (CIDR format)+ , sourceTags :: Maybe [String]+ -- ^ Source instance tags+ , targetTags :: Maybe [String]+ -- ^ Target instance tags+ , allowed :: Maybe [AllowedFirewall]+ -- ^ Traffic allow rules+ , denied :: Maybe [DeniedFirewall]+ -- ^ Traffic deny rules+ , direction :: Maybe String+ -- ^ Traffic direction (default: INGRESS)+ , logConfig :: Maybe LogConfig+ -- ^ Logging configuration+ , disabled :: Maybe Bool+ -- ^ Initial disabled state (default: False)+ }+ deriving (Show, Eq)++-- | Alias for operation response from firewall creation+type CreateFirewallResp = Operation++-- JSON Instances++instance FromJSON WarningData where+ parseJSON = withObject "WarningData" $ \v ->+ WarningData+ <$> v .: "key"+ <*> v .: "value"++instance ToJSON WarningData where+ toJSON (WarningData k v) =+ object+ [ "key" .= k+ , "value" .= v+ ]++instance FromJSON Warning where+ parseJSON = withObject "Warning" $ \v ->+ Warning+ <$> v .: "code"+ <*> v .: "message"+ <*> v .:? "data"++instance ToJSON Warning where+ toJSON (Warning c m d) =+ object+ [ "code" .= c+ , "message" .= m+ , "data" .= d+ ]++instance FromJSON LogConfig where+ parseJSON = withObject "LogConfig" $ \v ->+ LogConfig+ <$> v .: "enable"+ <*> v .:? "metadata"++instance ToJSON LogConfig where+ toJSON (LogConfig e m) =+ object+ [ "enable" .= e+ , "metadata" .= m+ ]++instance FromJSON AllowedFirewall where+ parseJSON = withObject "AllowedFirewall" $ \v ->+ AllowedFirewall+ <$> v .: "IPProtocol"+ <*> v .:? "ports"++instance ToJSON AllowedFirewall where+ toJSON (AllowedFirewall i p) =+ object+ [ "IPProtocol" .= i+ , "ports" .= p+ ]++instance FromJSON DeniedFirewall where+ parseJSON = withObject "DeniedFirewall" $ \v ->+ DeniedFirewall+ <$> v .: "IPProtocol"+ <*> v .:? "ports"++instance ToJSON DeniedFirewall where+ toJSON (DeniedFirewall i p) =+ object+ [ "IPProtocol" .= i+ , "ports" .= p+ ]++instance FromJSON FirewallMeta where+ parseJSON = withObject "FirewallMeta" $ \v ->+ FirewallMeta+ <$> v .:? "kind"+ <*> v .:? "id"+ <*> v .:? "creationTimestamp"+ <*> v .:? "name"+ <*> v .:? "description"+ <*> v .:? "network"+ <*> v .:? "priority"+ <*> v .:? "sourceRanges"+ <*> v .:? "sourceTags"+ <*> v .:? "targetTags"+ <*> v .:? "allowed"+ <*> v .:? "denied"+ <*> v .:? "direction"+ <*> v .:? "logConfig"+ <*> v .:? "disabled"+ <*> v .:? "selfLink"++instance ToJSON FirewallMeta where+ toJSON+ ( FirewallMeta+ k+ i+ c+ name_+ description_+ network_+ priority_+ sourceRanges_+ sourceTags_+ targetTags_+ allowed_+ denied_+ direction_+ logConfig_+ disabled_+ selfLink_+ ) =+ object+ [ "kind" .= k+ , "id" .= i+ , "creationTimestamp" .= c+ , "name" .= name_+ , "description" .= description_+ , "network" .= network_+ , "priority" .= priority_+ , "sourceRanges" .= sourceRanges_+ , "sourceTags" .= sourceTags_+ , "targetTags" .= targetTags_+ , "allowed" .= allowed_+ , "denied" .= denied_+ , "direction" .= direction_+ , "logConfig" .= logConfig_+ , "disabled" .= disabled_+ , "selfLink" .= selfLink_+ ]++instance FromJSON FirewallList where+ parseJSON = withObject "FirewallList" $ \v ->+ FirewallList+ <$> v .:? "kind"+ <*> v .:? "id"+ <*> v .:? "items"+ <*> v .:? "nextPageToken"+ <*> v .:? "selfLink"+ <*> v .:? "warning"++instance ToJSON FirewallList where+ toJSON (FirewallList 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 .:? "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_+ operationType_+ targetLink_+ status_+ progress_+ insertTime_+ startTime_+ endTime_+ error1+ warnings_+ selfLink_+ ) =+ object+ [ "id" .= id_+ , "name" .= name_+ , "operationType" .= operationType_+ , "targetLink" .= targetLink_+ , "status" .= status_+ , "progress" .= progress_+ , "insertTime" .= insertTime_+ , "startTime" .= startTime_+ , "endTime" .= endTime_+ , "error" .= error1+ , "warnings" .= warnings_+ , "selfLink" .= selfLink_+ ]++instance ToJSON CreateFirewallOps where+ toJSON+ ( CreateFirewallOps+ name_+ description_+ network_+ priority_+ sourceRanges_+ sourceTags_+ targetTags_+ allowed_+ denied_+ direction_+ logConfig_+ disabled_+ ) =+ object+ [ "name" .= name_+ , "description" .= description_+ , "network" .= network_+ , "priority" .= priority_+ , "sourceRanges" .= sourceRanges_+ , "sourceTags" .= sourceTags_+ , "targetTags" .= targetTags_+ , "allowed" .= allowed_+ , "denied" .= denied_+ , "direction" .= direction_+ , "logConfig" .= logConfig_+ , "disabled" .= disabled_+ ]++-- | Query parameters for listFirewalls+data ListFirewallsQuery = ListFirewallsQuery+ { filter0 :: Maybe String+ , maxResults :: Maybe Int+ , pageToken :: Maybe String+ , orderBy :: Maybe String+ }+ deriving (Show, Eq)++-- | Query parameters for createFirewall+data CreateFirewallQuery = CreateFirewallQuery+ { requestId :: Maybe String+ }+ deriving (Show, Eq)++-- Helper function to convert query to list of (ByteString, Maybe ByteString)+queryToList :: (a -> [(String, String)]) -> a -> [(BS.ByteString, Maybe BS.ByteString)]+queryToList f q = map (\(k, v) -> (BS8.pack k, Just $ BS8.pack v)) (f q)++-- Functions++{- | List firewall rules in a project++Example:++> listFirewalls "my-project" (Just ListFirewallsQuery { filter0 = Just "name=default-allow-*" })+-}+listFirewalls ::+ -- | GCP Project ID+ String ->+ -- | Optional query parameters+ Maybe ListFirewallsQuery ->+ -- | List result or error+ IO (Either String FirewallList)+listFirewalls project mbQuery = do+ let queryParams = maybe [] (queryToList queryToListFunc) mbQuery+ doRequestJSON+ RequestOptions+ { reqMethod = GET+ , reqUrl = googleComputeUrl+ , mbQueryParams = Just queryParams+ , mbReqBody = Nothing+ , mbReqHeaders = Nothing+ , mbReqPath = Just $ "/projects/" <> project <> "/global/firewalls"+ }+ where+ queryToListFunc :: ListFirewallsQuery -> [(String, String)]+ queryToListFunc q =+ catMaybes+ [ ("filter",) <$> filter0 q+ , ("maxResults",) . show <$> maxResults q+ , ("pageToken",) <$> pageToken q+ , ("orderBy",) <$> orderBy q+ ]++{- | Create a new firewall rule++Example:++> createFirewall "my-project"+> (defaultCreateFirewallOps "allow-http"+> { allowed = Just [AllowedFirewall "tcp" (Just ["80"])]+> , sourceRanges = Just ["0.0.0.0/0"]+> }) Nothing+-}+createFirewall ::+ -- | GCP Project ID+ String ->+ -- | Firewall configuration+ CreateFirewallOps ->+ -- | Optional request ID+ Maybe CreateFirewallQuery ->+ -- | Operation or error+ IO (Either String CreateFirewallResp)+createFirewall project createFirewallOps mbQuery = do+ let queryParams = maybe [] (queryToList queryToListFunc) mbQuery+ doRequestJSON+ RequestOptions+ { reqMethod = POST+ , reqUrl = googleComputeUrl+ , mbQueryParams = Just queryParams+ , mbReqBody = Just $ encode createFirewallOps+ , mbReqHeaders = Nothing+ , mbReqPath = Just $ "/projects/" <> project <> "/global/firewalls"+ }+ where+ queryToListFunc :: CreateFirewallQuery -> [(String, String)]+ queryToListFunc q =+ catMaybes+ [("requestId",) <$> requestId q]++-- | Default firewall creation options+--+-- Creates minimal valid configuration with required name.+-- Additional fields can be added using record update syntax:+--+-- > defaultCreateFirewallOps "web-allow" +-- > { description = Just "Allow web traffic"+-- > , allowed = Just [AllowedFirewall "tcp" (Just ["80","443"])]+-- > }+defaultCreateFirewallOps :: String -> CreateFirewallOps+defaultCreateFirewallOps firewallName =+ CreateFirewallOps+ { name = firewallName+ , description = Nothing+ , network = Nothing+ , priority = Nothing+ , sourceRanges = Nothing+ , sourceTags = Nothing+ , targetTags = Nothing+ , allowed = Nothing+ , denied = Nothing+ , direction = Nothing+ , logConfig = Nothing+ , disabled = Nothing+ }
+ src/Google/Cloud/Compute/Instance.hs view
@@ -0,0 +1,1145 @@+{-|+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"+ }
+ src/Google/Cloud/Compute/Network.hs view
@@ -0,0 +1,241 @@+{-|+Module : Google.Cloud.Compute.Network+Copyright : (c) 2025 Tushar+License : MIT+Maintainer : +Stability : experimental++This module provides types and functions for interacting with Google Cloud Platform (GCP)+Compute Engine Network. It supports common operations as listing networks,+along with detailed configuration network 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/networks GCP Compute Network Documentation>+-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}++module Google.Cloud.Compute.Network + ( -- * Network Operations+ listNetworks+ -- * Data Types+ , NetworkList(..)+ , NetworkMeta(..)+ , NetworkPeering(..)+ , ListNetworksQuery(..)+ -- * Defaults+ , defaultListNetworksQuery+ ) where++import Data.Aeson+import Data.Text (Text)+import Google.Cloud.Common.Core+import Google.Cloud.Compute.Common+import Data.Maybe (catMaybes)+import qualified Data.ByteString.Char8 as BS8++-- | Response structure for listNetworks operation+data NetworkList = NetworkList+ { kind :: Text+ -- ^ Type of resource (always "compute#networkList")+ , id_ :: Text+ -- ^ Unique identifier for the resource+ , items :: [NetworkMeta]+ -- ^ List of network resources+ , nextPageToken :: Maybe Text+ -- ^ Pagination token for next page+ , selfLink :: Text+ -- ^ Server-defined URL for the resource+ } deriving (Eq, Show)++instance FromJSON NetworkList where+ parseJSON = withObject "NetworkList" $ \v -> NetworkList+ <$> v .: "kind"+ <*> v .: "id"+ <*> v .: "items"+ <*> v .:? "nextPageToken"+ <*> v .: "selfLink"++instance ToJSON NetworkList where+ toJSON NetworkList{..} = object+ [ "kind" .= kind+ , "id" .= id_+ , "items" .= items + , "nextPageToken" .= nextPageToken + , "selfLink" .= selfLink + ]++-- | Detailed network resource metadata+data NetworkMeta = NetworkMeta+ { kind :: Text+ -- ^ Type of resource (always "compute#network")+ , id_ :: Text+ -- ^ Unique identifier for the network+ , creationTimestamp :: Text+ -- ^ Creation timestamp in RFC3339 format+ , name :: Text+ -- ^ Name of the network+ , description :: Maybe Text+ -- ^ User-provided description+ , autoCreateSubnetworks :: Maybe Bool+ -- ^ Whether to auto-create subnetworks+ , subnetworks :: [Text]+ -- ^ List of subnetworks URLs in this network+ , iPv4Range :: Maybe Text+ -- ^ IPv4 range for legacy networks+ , iPv6Range :: Maybe Text+ -- ^ IPv6 range for legacy networks+ , gatewayIPv4 :: Maybe Text+ -- ^ Gateway IPv4 address+ , mtu :: Maybe Int+ -- ^ Maximum transmission unit+ , peerings :: Maybe [NetworkPeering]+ -- ^ List of network peerings+ , selfLink :: Text+ -- ^ Server-defined URL for the resource+ , enableUlaInternalIpv6 :: Maybe Bool+ -- ^ ULA internal IPv6 support+ , internalIpv6Range :: Maybe Text+ -- ^ Internal IPv6 range+ , networkFirewallPolicyEnforcementOrder :: Maybe Text+ -- ^ Firewall policy order+ } deriving (Eq, Show)++instance FromJSON NetworkMeta where+ parseJSON = withObject "NetworkMeta" $ \v -> NetworkMeta+ <$> v .: "kind"+ <*> v .: "id"+ <*> v .: "creationTimestamp"+ <*> v .: "name"+ <*> v .:? "description"+ <*> v .:? "autoCreateSubnetworks"+ <*> v .: "subnetworks"+ <*> v .:? "IPv4Range" -- Match API's casing+ <*> v .:? "IPv6Range"+ <*> v .:? "gatewayIPv4"+ <*> v .:? "mtu"+ <*> (v .:? "peerings")+ <*> v .: "selfLink"+ <*> v .:? "enableUlaInternalIpv6"+ <*> v .:? "internalIpv6Range"+ <*> v .:? "networkFirewallPolicyEnforcementOrder"++instance ToJSON NetworkMeta where+ toJSON NetworkMeta{..} = object $ catMaybes+ [ Just ("kind" .= kind)+ , Just ("id" .= id_)+ , Just ("creationTimestamp" .= creationTimestamp )+ , Just ("name" .= name )+ , ("description" .=) <$> description + , ("autoCreateSubnetworks" .=) <$> autoCreateSubnetworks+ , Just ("subnetworks" .= subnetworks)+ , ("IPv4Range" .=) <$> iPv4Range + , ("IPv6Range" .=) <$> iPv6Range + , ("gatewayIPv4" .=) <$> gatewayIPv4 + , ("mtu" .=) <$> mtu + , Just ("peerings" .= peerings )+ , Just ("selfLink" .= selfLink )+ , ("enableUlaInternalIpv6" .=) <$> enableUlaInternalIpv6 + , ("internalIpv6Range" .=) <$> internalIpv6Range + , ("networkFirewallPolicyEnforcementOrder" .=) <$> networkFirewallPolicyEnforcementOrder + ]++-- | Network peering configuration+data NetworkPeering = NetworkPeering+ { name :: Text+ -- ^ Name of the peering+ , network :: Text+ -- ^ URL of peer network+ , state :: Text+ -- ^ Peering state (ACTIVE/INACTIVE)+ , stateDetails :: Maybe Text+ -- ^ Detailed state information+ , exchangeSubnetRoutes :: Maybe Bool+ -- ^ Subnet route exchange+ , exportCustomRoutes :: Maybe Bool+ -- ^ Custom route export+ , importCustomRoutes :: Maybe Bool+ -- ^ Custom route import+ } deriving (Eq, Show)++instance FromJSON NetworkPeering where+ parseJSON = withObject "NetworkPeering" $ \v -> NetworkPeering+ <$> v .: "name"+ <*> v .: "network"+ <*> v .: "state"+ <*> v .:? "stateDetails"+ <*> v .:? "exchangeSubnetRoutes"+ <*> v .:? "exportCustomRoutes"+ <*> v .:? "importCustomRoutes"++instance ToJSON NetworkPeering where+ toJSON NetworkPeering{..} = object $ catMaybes+ [ Just ("name" .= name)+ , Just ("network" .= network)+ , Just ("state" .= state)+ , ("stateDetails" .=) <$> stateDetails+ , ("exchangeSubnetRoutes" .=) <$> exchangeSubnetRoutes+ , ("exportCustomRoutes" .=) <$> exportCustomRoutes+ , ("importCustomRoutes" .=) <$> importCustomRoutes+ ]++-- | Query parameters for listNetworks operation+data ListNetworksQuery = ListNetworksQuery+ { filter_ :: Maybe String+ -- ^ Filter expression+ , maxResults :: Maybe Int+ -- ^ Maximum results per page+ , orderBy :: Maybe String+ -- ^ Sort order+ , pageToken :: Maybe String+ -- ^ Page token+ , returnPartialSuccess :: Maybe Bool+ -- ^ Partial success return+ } deriving (Show)++-- | Default list networks query parameters+defaultListNetworksQuery :: ListNetworksQuery+defaultListNetworksQuery = ListNetworksQuery+ { filter_ = Nothing+ , maxResults = Nothing+ , orderBy = Nothing+ , pageToken = Nothing+ , returnPartialSuccess = Nothing+ }++-- | List networks in a project+listNetworks ::+ String -- ^ GCP Project ID+ -> Maybe ListNetworksQuery -- ^ Query parameters+ -> IO (Either String NetworkList)+listNetworks project mbQuery = do+ let queryParams = maybe [] toQueryList mbQuery+ doRequestJSON+ RequestOptions+ { reqMethod = GET+ , reqUrl = googleComputeUrl+ , mbQueryParams = Just queryParams+ , mbReqBody = Nothing+ , mbReqHeaders = Nothing+ , mbReqPath = Just $ toPath+ [ "projects"+ , project+ , "global"+ , "networks"+ ]+ }+ where+ toQueryList :: ListNetworksQuery -> [(BS8.ByteString, Maybe BS8.ByteString)]+ toQueryList ListNetworksQuery{..} =+ catMaybes+ [ ("filter",) . Just . BS8.pack <$> filter_+ , ("maxResults",) . Just . BS8.pack . show <$> maxResults+ , ("orderBy",) . Just . BS8.pack <$> orderBy+ , ("pageToken",) . Just . BS8.pack <$> pageToken+ , ("returnPartialSuccess",) . Just . BS8.pack . show <$> returnPartialSuccess+ ]
+ test/Spec.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++import Test.Hspec+import Google.Cloud.Compute.Disk as Disk+import Google.Cloud.Compute.Instance as Instance+import qualified Google.Cloud.Compute.Firewall as Firewall+import Data.Aeson (decode, encode)+import Data.Maybe (isJust)+import qualified Data.ByteString.Lazy.Char8 as BSL++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "Google.Cloud.Compute.Disk" $ do+ context "DiskResponse JSON serialization" $ do+ it "successfully parses valid JSON to DiskResponse" $ do+ let json = "{\"kind\":\"compute#diskList\",\"items\":[],\"nextPageToken\":\"token\",\"selfLink\":\"link\"}"+ let result = decode (BSL.pack json) :: Maybe DiskResponse+ result `shouldSatisfy` isJust++ it "successfully converts DiskResponse to JSON" $ do+ let diskResponse = DiskResponse (Just "compute#diskList") Nothing (Just []) (Just "token") (Just "link") Nothing+ let json = encode diskResponse+ decode json `shouldBe` Just diskResponse++ context "InsertDiskOps default" $ do+ it "creates a default InsertDiskOps" $ do+ let diskOps = defaultInsertOps "my-disk" "100"+ (\InsertDiskOps{..} -> name) diskOps `shouldBe` "my-disk"+ (\InsertDiskOps{..} -> sizeGb) diskOps `shouldBe` "100"+ (\InsertDiskOps{..} -> description) diskOps `shouldBe` Nothing++ context "CreateSnapshotOps default" $ do+ it "creates a default CreateSnapshotOps" $ do+ let snapshotOps = defaultCreateSnapshotOps "my-snapshot"+ (\CreateSnapshotOps{..} -> name) snapshotOps `shouldBe` "my-snapshot"+ (\CreateSnapshotOps{..} -> description) snapshotOps `shouldBe` Nothing++ context "Warning JSON serialization" $ do+ it "serializes and deserializes Warning correctly" $ do+ let warning = Disk.Warning "NO_RESULTS" "No results found" (Just [WarningData "scope" "global"])+ let json = encode warning+ decode json `shouldBe` Just warning++ context "Operation JSON serialization" $ do+ it "successfully parses valid JSON to Operation" $ do+ let json = "{\"id\":\"123\",\"name\":\"operation-1\",\"status\":\"DONE\"}"+ let result = decode (BSL.pack json) :: Maybe Operation+ result `shouldSatisfy` isJust++ it "successfully converts Operation to JSON" $ do+ let operation = Operation (Just "123") (Just "operation-1") Nothing Nothing Nothing (Just "DONE") Nothing Nothing Nothing Nothing Nothing Nothing Nothing+ let json = encode operation+ decode json `shouldBe` Just operation++ describe "JSON Serialization" $ do+ it "correctly serializes and deserializes RunDuration" $ do+ let runDuration = TerminationTime "2023-10-15T10:00:00Z"+ decode (encode runDuration) `shouldBe` Just runDuration++ it "correctly serializes and deserializes OnInstanceTerminationAction" $ do+ let action = DiscardLocalSsd True+ decode (encode action) `shouldBe` Just action++ it "correctly serializes and deserializes Scheduling" $ do+ let scheduling = Scheduling {+ onHostMaintenance = "MIGRATE",+ automaticRestart = True,+ preemptible = False,+ nodeAffinities = [],+ minNodeCpus = 1,+ locationHint = "us-central1",+ availabilityDomain = 0,+ provisioningModel = "STANDARD",+ instanceTerminationAction = RunDurationRunDuration (TerminationTime "2023-10-15T12:00:00Z")+ }+ decode (encode scheduling) `shouldBe` Just scheduling++ describe "Default Query Constructors" $ do+ it "creates a default ListInstancesQuery with all fields set to Nothing" $ do+ let query = defaultListInstancesQuery+ filter_ query `shouldBe` Nothing+ maxResults query `shouldBe` Nothing+ orderBy query `shouldBe` Nothing+ pageToken query `shouldBe` Nothing+ returnPartialSuccess query `shouldBe` Nothing++ it "creates a default InsertInstanceOps with correct settings" $ do+ let ops = defaultInsertInstanceOps "my-project" "us-central1-a" "test-instance" "f1-micro"+ (\InsertInstanceOps{..} -> name) ops `shouldBe` "test-instance"+ machineType ops `shouldBe` "zones/us-central1-a/machineTypes/f1-micro"+ isJust (disks ops) `shouldBe` True+ isJust (networkInterfaces ops) `shouldBe` True++ it "creates a default RequestIdQuery with given requestId" $ do+ let rid = "request-123"+ let query = defaultRequestIdQuery rid+ requestId query `shouldBe` Just rid+ + describe "Google.Cloud.Compute.Firewall" $ do++ describe "JSON serialization/deserialization" $ do+ it "should serialize and deserialize WarningData" $ do+ let warningData = Firewall.WarningData "some_key" "some_value"+ let json = encode warningData+ decode json `shouldBe` Just warningData++ it "should serialize and deserialize Warning" $ do+ let warning = Firewall.Warning "some_code" "some_message" Nothing+ let json = encode warning+ decode json `shouldBe` Just warning++ it "should serialize and deserialize LogConfig" $ do+ let logConfig = Firewall.LogConfig True (Just "INCLUDE_ALL_METADATA")+ let json = encode logConfig+ decode json `shouldBe` Just logConfig++ it "should serialize and deserialize AllowedFirewall" $ do+ let allowedFirewall = Firewall.AllowedFirewall "tcp" (Just ["80", "443"])+ let json = encode allowedFirewall+ decode json `shouldBe` Just allowedFirewall++ it "should serialize and deserialize DeniedFirewall" $ do+ let deniedFirewall = Firewall.DeniedFirewall "tcp" (Just ["22"])+ let json = encode deniedFirewall+ decode json `shouldBe` Just deniedFirewall++ it "should serialize and deserialize FirewallMeta" $ do+ let firewallMeta = Firewall.FirewallMeta (Just "compute#firewall") (Just "123456") (Just "2021-01-01T00:00:00Z") + (Just "test-firewall") (Just "A test firewall") + (Just "global/networks/default") (Just 1000) + (Just ["0.0.0.0/0"]) (Just ["tag1"]) (Just ["tag2"]) + (Just [Firewall.AllowedFirewall "tcp" (Just ["80"])]) + (Just [Firewall.DeniedFirewall "tcp" (Just ["22"])]) + (Just "INGRESS") (Just (Firewall.LogConfig True (Just "INCLUDE_ALL_METADATA"))) + (Just False) (Just "selfLink")+ let json = encode firewallMeta+ decode json `shouldBe` Just firewallMeta++ it "should serialize and deserialize FirewallList" $ do+ let firewallList = Firewall.FirewallList (Just "compute#firewallList") (Just "12345") + (Just [Firewall.FirewallMeta (Just "compute#firewall") (Just "123456") + (Just "2021-01-01T00:00:00Z") (Just "test-firewall") + (Just "A test firewall") (Just "global/networks/default") + (Just 1000) (Just ["0.0.0.0/0"]) (Just ["tag1"]) + (Just ["tag2"]) (Just [Firewall.AllowedFirewall "tcp" (Just ["80"])] ) + (Just [Firewall.DeniedFirewall "tcp" (Just ["22"])] ) + (Just "INGRESS") (Just (Firewall.LogConfig True (Just "INCLUDE_ALL_METADATA"))) + (Just False) (Just "selfLink")]) + (Just "nextPageToken") (Just "selfLink") + (Just (Firewall.Warning "some_code" "some_message" Nothing))+ let json = encode firewallList+ decode json `shouldBe` Just firewallList++ it "should create default CreateFirewallOps" $ do+ let defaultOps = Firewall.defaultCreateFirewallOps "test-firewall"+ (\Firewall.CreateFirewallOps{..} -> name) defaultOps `shouldBe` "test-firewall"+ (\Firewall.CreateFirewallOps{..} -> description) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> network) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> priority) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> sourceRanges) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> sourceTags) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> targetTags) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> allowed) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> denied) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> direction) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> logConfig) defaultOps `shouldBe` Nothing+ (\Firewall.CreateFirewallOps{..} -> disabled) defaultOps `shouldBe` Nothing