2022-03-22 11:03:15 -04:00
package client
import (
"context"
"crypto/rand"
2022-08-09 04:26:29 -04:00
"encoding/json"
2022-03-22 11:03:15 -04:00
"fmt"
"math/big"
compute "cloud.google.com/go/compute/apiv1"
admin "cloud.google.com/go/iam/admin/apiv1"
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
2022-06-07 05:08:44 -04:00
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
2022-06-08 02:17:52 -04:00
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
2022-03-22 11:03:15 -04:00
"github.com/edgelesssys/constellation/internal/state"
2022-08-01 05:54:29 -04:00
"go.uber.org/multierr"
2022-08-09 04:26:29 -04:00
"golang.org/x/oauth2/google"
2022-03-22 11:03:15 -04:00
)
// Client is a client for the Google Compute Engine.
type Client struct {
instanceAPI
operationRegionAPI
operationZoneAPI
operationGlobalAPI
networksAPI
subnetworksAPI
firewallsAPI
2022-06-09 16:26:36 -04:00
forwardingRulesAPI
backendServicesAPI
healthChecksAPI
2022-03-22 11:03:15 -04:00
instanceTemplateAPI
instanceGroupManagersAPI
iamAPI
projectsAPI
2022-06-29 09:26:29 -04:00
workers cloudtypes . Instances
controlPlanes cloudtypes . Instances
workerInstanceGroup string
controlPlaneInstanceGroup string
controlPlaneTemplate string
workerTemplate string
network string
subnetwork string
secondarySubnetworkRange string
firewalls [ ] string
name string
project string
uid string
zone string
region string
serviceAccount string
2022-06-09 16:26:36 -04:00
// loadbalancer
healthCheck string
backendService string
forwardingRule string
2022-03-22 11:03:15 -04:00
}
// NewFromDefault creates an uninitialized client.
func NewFromDefault ( ctx context . Context ) ( * Client , error ) {
var closers [ ] closer
insAPI , err := compute . NewInstancesRESTClient ( ctx )
if err != nil {
return nil , err
}
closers = append ( closers , insAPI )
opZoneAPI , err := compute . NewZoneOperationsRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , opZoneAPI )
opRegionAPI , err := compute . NewRegionOperationsRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , opRegionAPI )
opGlobalAPI , err := compute . NewGlobalOperationsRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , opGlobalAPI )
netAPI , err := compute . NewNetworksRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , netAPI )
subnetAPI , err := compute . NewSubnetworksRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , subnetAPI )
fwAPI , err := compute . NewFirewallsRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
2022-06-09 16:26:36 -04:00
closers = append ( closers , subnetAPI )
forwardingRulesAPI , err := compute . NewForwardingRulesRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , forwardingRulesAPI )
backendServicesAPI , err := compute . NewRegionBackendServicesRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , backendServicesAPI )
targetPoolsAPI , err := compute . NewTargetPoolsRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , targetPoolsAPI )
healthChecksAPI , err := compute . NewRegionHealthChecksRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , healthChecksAPI )
2022-03-22 11:03:15 -04:00
templAPI , err := compute . NewInstanceTemplatesRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , templAPI )
groupAPI , err := compute . NewInstanceGroupManagersRESTClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , groupAPI )
iamAPI , err := admin . NewIamClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
closers = append ( closers , iamAPI )
projectsAPI , err := resourcemanager . NewProjectsClient ( ctx )
if err != nil {
_ = closeAll ( closers )
return nil , err
}
return & Client {
instanceAPI : & instanceClient { insAPI } ,
operationRegionAPI : opRegionAPI ,
operationZoneAPI : opZoneAPI ,
operationGlobalAPI : opGlobalAPI ,
networksAPI : & networksClient { netAPI } ,
subnetworksAPI : & subnetworksClient { subnetAPI } ,
firewallsAPI : & firewallsClient { fwAPI } ,
2022-06-09 16:26:36 -04:00
forwardingRulesAPI : & forwardingRulesClient { forwardingRulesAPI } ,
backendServicesAPI : & backendServicesClient { backendServicesAPI } ,
healthChecksAPI : & healthChecksClient { healthChecksAPI } ,
2022-03-22 11:03:15 -04:00
instanceTemplateAPI : & instanceTemplateClient { templAPI } ,
instanceGroupManagersAPI : & instanceGroupManagersClient { groupAPI } ,
iamAPI : & iamClient { iamAPI } ,
projectsAPI : & projectsClient { projectsAPI } ,
2022-06-29 09:26:29 -04:00
workers : make ( cloudtypes . Instances ) ,
controlPlanes : make ( cloudtypes . Instances ) ,
2022-03-22 11:03:15 -04:00
} , nil
}
// NewInitialized creates an initialized client.
func NewInitialized ( ctx context . Context , project , zone , region , name string ) ( * Client , error ) {
2022-08-09 04:26:29 -04:00
// check if ADC are configured for the same project as the cluster
var defaultProject string
creds , err := google . FindDefaultCredentials ( ctx )
if err != nil {
return nil , err
}
// if the CLI is run by a service account, use the project of the service account
defaultProject = creds . ProjectID
// if the CLI is run by a user directly projectID will be empty, use the quota project id of the user instead
if defaultProject == "" {
var projectID struct {
ProjectID string ` json:"quota_project_id" `
}
if err := json . Unmarshal ( creds . JSON , & projectID ) ; err != nil {
return nil , err
}
defaultProject = projectID . ProjectID
}
if defaultProject != project {
return nil , fmt . Errorf ( "application default credentials are configured for project %q, but the cluster is configured for project %q" , defaultProject , project )
}
2022-03-22 11:03:15 -04:00
client , err := NewFromDefault ( ctx )
if err != nil {
return nil , err
}
err = client . init ( project , zone , region , name )
return client , err
}
// Close closes the client's connection.
func ( c * Client ) Close ( ) error {
closers := [ ] closer {
c . instanceAPI ,
2022-06-09 16:26:36 -04:00
c . operationRegionAPI ,
2022-03-22 11:03:15 -04:00
c . operationZoneAPI ,
c . operationGlobalAPI ,
c . networksAPI ,
2022-06-09 16:26:36 -04:00
c . subnetworksAPI ,
2022-03-22 11:03:15 -04:00
c . firewallsAPI ,
2022-06-09 16:26:36 -04:00
c . forwardingRulesAPI ,
c . backendServicesAPI ,
c . healthChecksAPI ,
2022-03-22 11:03:15 -04:00
c . instanceTemplateAPI ,
c . instanceGroupManagersAPI ,
2022-06-09 16:26:36 -04:00
c . iamAPI ,
c . projectsAPI ,
2022-03-22 11:03:15 -04:00
}
return closeAll ( closers )
}
// init initializes the client.
func ( c * Client ) init ( project , zone , region , name string ) error {
c . project = project
c . zone = zone
c . name = name
c . region = region
uid , err := c . generateUID ( )
if err != nil {
return err
}
c . uid = uid
return nil
}
// GetState returns the state of the client as ConstellationState.
2022-08-01 06:35:35 -04:00
func ( c * Client ) GetState ( ) state . ConstellationState {
return state . ConstellationState {
Name : c . name ,
UID : c . uid ,
CloudProvider : cloudprovider . GCP . String ( ) ,
BootstrapperHost : c . controlPlanes . PublicIPs ( ) [ 0 ] ,
GCPProject : c . project ,
GCPZone : c . zone ,
GCPRegion : c . region ,
GCPWorkerInstances : c . workers ,
GCPWorkerInstanceGroup : c . workerInstanceGroup ,
GCPWorkerInstanceTemplate : c . workerTemplate ,
GCPControlPlaneInstances : c . controlPlanes ,
GCPControlPlaneInstanceGroup : c . controlPlaneInstanceGroup ,
GCPControlPlaneInstanceTemplate : c . controlPlaneTemplate ,
GCPFirewalls : c . firewalls ,
GCPNetwork : c . network ,
GCPSubnetwork : c . subnetwork ,
GCPHealthCheck : c . healthCheck ,
GCPBackendService : c . backendService ,
GCPForwardingRule : c . forwardingRule ,
GCPServiceAccount : c . serviceAccount ,
2022-03-22 11:03:15 -04:00
}
}
// SetState sets the state of the client to the handed ConstellationState.
2022-08-01 06:35:35 -04:00
func ( c * Client ) SetState ( stat state . ConstellationState ) {
2022-07-29 02:10:51 -04:00
c . workers = stat . GCPWorkerInstances
c . controlPlanes = stat . GCPControlPlaneInstances
2022-06-29 09:26:29 -04:00
c . workerInstanceGroup = stat . GCPWorkerInstanceGroup
c . controlPlaneInstanceGroup = stat . GCPControlPlaneInstanceGroup
2022-03-22 11:03:15 -04:00
c . project = stat . GCPProject
c . zone = stat . GCPZone
c . region = stat . GCPRegion
c . name = stat . Name
c . uid = stat . UID
c . firewalls = stat . GCPFirewalls
c . network = stat . GCPNetwork
c . subnetwork = stat . GCPSubnetwork
2022-06-29 09:26:29 -04:00
c . workerTemplate = stat . GCPWorkerInstanceTemplate
c . controlPlaneTemplate = stat . GCPControlPlaneInstanceTemplate
2022-06-09 16:26:36 -04:00
c . healthCheck = stat . GCPHealthCheck
c . backendService = stat . GCPBackendService
c . forwardingRule = stat . GCPForwardingRule
2022-03-22 11:03:15 -04:00
c . serviceAccount = stat . GCPServiceAccount
}
func ( c * Client ) generateUID ( ) ( string , error ) {
letters := [ ] byte ( "abcdefghijklmnopqrstuvwxyz0123456789" )
const uidLen = 5
uid := make ( [ ] byte , uidLen )
for i := 0 ; i < uidLen ; i ++ {
n , err := rand . Int ( rand . Reader , big . NewInt ( int64 ( len ( letters ) ) ) )
if err != nil {
return "" , err
}
uid [ i ] = letters [ n . Int64 ( ) ]
}
return string ( uid ) , nil
}
type closer interface {
Close ( ) error
}
// closeAll closes all closers, even if an error occurs.
//
// Errors are collected and a composed error is returned.
func closeAll ( closers [ ] closer ) error {
// Since this function is intended to be deferred, it will always call all
// close operations, even if a previous operation failed. The if multiple
// errors occur, the returned error will be composed of the error messages
// of those errors.
2022-08-01 05:54:29 -04:00
var err error
2022-03-22 11:03:15 -04:00
for _ , closer := range closers {
2022-08-01 05:54:29 -04:00
err = multierr . Append ( err , closer . Close ( ) )
2022-03-22 11:03:15 -04:00
}
2022-08-01 05:54:29 -04:00
return err
2022-03-22 11:03:15 -04:00
}