mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 15:09:39 -05:00
Remove GCP client from CLI
This commit is contained in:
parent
d973740b03
commit
feffe40987
@ -1,13 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package gcp
|
||||
|
||||
import "fmt"
|
||||
|
||||
func AutoscalingNodeGroup(project string, zone string, nodeInstanceGroup string, min int, max int) string {
|
||||
return fmt.Sprintf("%d:%d:https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instanceGroups/%s", min, max, project, zone, nodeInstanceGroup)
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
|
||||
func TestAutoscalingNodeGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
nodeGroups := AutoscalingNodeGroup("some-project", "some-zone", "some-group", 0, 100)
|
||||
wantNodeGroups := "0:100:https://www.googleapis.com/compute/v1/projects/some-project/zones/some-zone/instanceGroups/some-group"
|
||||
assert.Equal(wantNodeGroups, nodeGroups)
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
adminpb "google.golang.org/genproto/googleapis/iam/admin/v1"
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
)
|
||||
|
||||
type instanceAPI interface {
|
||||
Close() error
|
||||
List(ctx context.Context, req *computepb.ListInstancesRequest,
|
||||
opts ...gax.CallOption) InstanceIterator
|
||||
}
|
||||
|
||||
type operationRegionAPI interface {
|
||||
Close() error
|
||||
Wait(ctx context.Context, req *computepb.WaitRegionOperationRequest,
|
||||
opts ...gax.CallOption) (*computepb.Operation, error)
|
||||
}
|
||||
|
||||
type operationZoneAPI interface {
|
||||
Close() error
|
||||
Wait(ctx context.Context, req *computepb.WaitZoneOperationRequest,
|
||||
opts ...gax.CallOption) (*computepb.Operation, error)
|
||||
}
|
||||
|
||||
type operationGlobalAPI interface {
|
||||
Close() error
|
||||
Wait(ctx context.Context, req *computepb.WaitGlobalOperationRequest,
|
||||
opts ...gax.CallOption) (*computepb.Operation, error)
|
||||
}
|
||||
|
||||
type firewallsAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteFirewallRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertFirewallRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type forwardingRulesAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Get(ctx context.Context, req *computepb.GetGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption) (*computepb.ForwardingRule, error)
|
||||
SetLabels(ctx context.Context, req *computepb.SetLabelsGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type backendServicesAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteBackendServiceRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertBackendServiceRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type healthChecksAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteHealthCheckRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertHealthCheckRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type targetTCPProxiesAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type networksAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteNetworkRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertNetworkRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type subnetworksAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteSubnetworkRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertSubnetworkRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type instanceTemplateAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type instanceGroupManagersAPI interface {
|
||||
Close() error
|
||||
Delete(ctx context.Context, req *computepb.DeleteInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
ListManagedInstances(ctx context.Context, req *computepb.ListManagedInstancesInstanceGroupManagersRequest,
|
||||
opts ...gax.CallOption) ManagedInstanceIterator
|
||||
}
|
||||
|
||||
type iamAPI interface {
|
||||
Close() error
|
||||
CreateServiceAccount(ctx context.Context, req *adminpb.CreateServiceAccountRequest,
|
||||
opts ...gax.CallOption) (*adminpb.ServiceAccount, error)
|
||||
CreateServiceAccountKey(ctx context.Context, req *adminpb.CreateServiceAccountKeyRequest,
|
||||
opts ...gax.CallOption) (*adminpb.ServiceAccountKey, error)
|
||||
DeleteServiceAccount(ctx context.Context, req *adminpb.DeleteServiceAccountRequest,
|
||||
opts ...gax.CallOption) error
|
||||
}
|
||||
|
||||
type projectsAPI interface {
|
||||
Close() error
|
||||
GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest,
|
||||
opts ...gax.CallOption) (*iampb.Policy, error)
|
||||
SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest,
|
||||
opts ...gax.CallOption) (*iampb.Policy, error)
|
||||
}
|
||||
|
||||
type addressesAPI interface {
|
||||
Close() error
|
||||
Insert(ctx context.Context, req *computepb.InsertGlobalAddressRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Get(ctx context.Context, req *computepb.GetGlobalAddressRequest,
|
||||
opts ...gax.CallOption) (*computepb.Address, error)
|
||||
Delete(ctx context.Context, req *computepb.DeleteGlobalAddressRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type Operation interface {
|
||||
Proto() *computepb.Operation
|
||||
}
|
||||
|
||||
type ManagedInstanceIterator interface {
|
||||
Next() (*computepb.ManagedInstance, error)
|
||||
}
|
||||
|
||||
type InstanceIterator interface {
|
||||
Next() (*computepb.Instance, error)
|
||||
}
|
@ -1,576 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
"google.golang.org/api/iterator"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type stubOperation struct {
|
||||
*computepb.Operation
|
||||
}
|
||||
|
||||
func (o *stubOperation) Proto() *computepb.Operation {
|
||||
return o.Operation
|
||||
}
|
||||
|
||||
type stubInstanceAPI struct {
|
||||
listIterator *stubInstanceIterator
|
||||
}
|
||||
|
||||
func (a stubInstanceAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceAPI) List(ctx context.Context, req *computepb.ListInstancesRequest,
|
||||
opts ...gax.CallOption,
|
||||
) InstanceIterator {
|
||||
return a.listIterator
|
||||
}
|
||||
|
||||
type stubInstanceIterator struct {
|
||||
instances []*computepb.Instance
|
||||
nextErr error
|
||||
|
||||
internalCounter int
|
||||
}
|
||||
|
||||
func (i *stubInstanceIterator) Next() (*computepb.Instance, error) {
|
||||
if i.nextErr != nil {
|
||||
return nil, i.nextErr
|
||||
}
|
||||
if i.internalCounter >= len(i.instances) {
|
||||
i.internalCounter = 0
|
||||
return nil, iterator.Done
|
||||
}
|
||||
resp := i.instances[i.internalCounter]
|
||||
i.internalCounter++
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type stubOperationZoneAPI struct {
|
||||
waitErr error
|
||||
}
|
||||
|
||||
func (a stubOperationZoneAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubOperationZoneAPI) Wait(ctx context.Context, req *computepb.WaitZoneOperationRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Operation, error) {
|
||||
if a.waitErr != nil {
|
||||
return nil, a.waitErr
|
||||
}
|
||||
return &computepb.Operation{
|
||||
Status: computepb.Operation_DONE.Enum(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubOperationRegionAPI struct {
|
||||
waitErr error
|
||||
}
|
||||
|
||||
func (a stubOperationRegionAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubOperationRegionAPI) Wait(ctx context.Context, req *computepb.WaitRegionOperationRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Operation, error) {
|
||||
if a.waitErr != nil {
|
||||
return nil, a.waitErr
|
||||
}
|
||||
return &computepb.Operation{
|
||||
Status: computepb.Operation_DONE.Enum(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubOperationGlobalAPI struct {
|
||||
waitErr error
|
||||
}
|
||||
|
||||
func (a stubOperationGlobalAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubOperationGlobalAPI) Wait(ctx context.Context, req *computepb.WaitGlobalOperationRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Operation, error) {
|
||||
if a.waitErr != nil {
|
||||
return nil, a.waitErr
|
||||
}
|
||||
return &computepb.Operation{
|
||||
Status: computepb.Operation_DONE.Enum(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubFirewallsAPI struct {
|
||||
deleteErr error
|
||||
insertErr error
|
||||
}
|
||||
|
||||
func (a stubFirewallsAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubFirewallsAPI) Delete(ctx context.Context, req *computepb.DeleteFirewallRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubFirewallsAPI) Insert(ctx context.Context, req *computepb.InsertFirewallRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubNetworksAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubNetworksAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubNetworksAPI) Insert(ctx context.Context, req *computepb.InsertNetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubNetworksAPI) Delete(ctx context.Context, req *computepb.DeleteNetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubSubnetworksAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubSubnetworksAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubSubnetworksAPI) Insert(ctx context.Context, req *computepb.InsertSubnetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
Region: proto.String("region"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubSubnetworksAPI) Delete(ctx context.Context, req *computepb.DeleteSubnetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
Region: proto.String("region"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubBackendServicesAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubBackendServicesAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubBackendServicesAPI) Insert(ctx context.Context, req *computepb.InsertBackendServiceRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubBackendServicesAPI) Delete(ctx context.Context, req *computepb.DeleteBackendServiceRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubTargetTCPProxiesAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubTargetTCPProxiesAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubTargetTCPProxiesAPI) Insert(ctx context.Context, req *computepb.InsertTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubTargetTCPProxiesAPI) Delete(ctx context.Context, req *computepb.DeleteTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubForwardingRulesAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
getErr error
|
||||
setLabelErr error
|
||||
forwardingRule *computepb.ForwardingRule
|
||||
}
|
||||
|
||||
func (a stubForwardingRulesAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubForwardingRulesAPI) Insert(ctx context.Context, req *computepb.InsertGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubForwardingRulesAPI) Delete(ctx context.Context, req *computepb.DeleteGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubForwardingRulesAPI) Get(ctx context.Context, req *computepb.GetGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.ForwardingRule, error) {
|
||||
if a.getErr != nil {
|
||||
return nil, a.getErr
|
||||
}
|
||||
return a.forwardingRule, nil
|
||||
}
|
||||
|
||||
func (a stubForwardingRulesAPI) SetLabels(ctx context.Context, req *computepb.SetLabelsGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.setLabelErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubHealthChecksAPI struct {
|
||||
insertErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubHealthChecksAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubHealthChecksAPI) Insert(ctx context.Context, req *computepb.InsertHealthCheckRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubHealthChecksAPI) Delete(ctx context.Context, req *computepb.DeleteHealthCheckRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubInstanceTemplateAPI struct {
|
||||
deleteErr error
|
||||
insertErr error
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubInstanceGroupManagersAPI struct {
|
||||
listIterator *stubManagedInstanceIterator
|
||||
|
||||
deleteErr error
|
||||
insertErr error
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) Delete(ctx context.Context, req *computepb.DeleteInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Zone: proto.String("zone"),
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) Insert(ctx context.Context, req *computepb.InsertInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Zone: proto.String("zone"),
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) ListManagedInstances(ctx context.Context, req *computepb.ListManagedInstancesInstanceGroupManagersRequest,
|
||||
opts ...gax.CallOption,
|
||||
) ManagedInstanceIterator {
|
||||
return a.listIterator
|
||||
}
|
||||
|
||||
type stubProjectsAPI struct {
|
||||
getPolicyErr error
|
||||
setPolicyErr error
|
||||
}
|
||||
|
||||
func (a stubProjectsAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubProjectsAPI) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
|
||||
if a.getPolicyErr != nil {
|
||||
return nil, a.getPolicyErr
|
||||
}
|
||||
return &iampb.Policy{
|
||||
Version: 3,
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{
|
||||
"member",
|
||||
},
|
||||
},
|
||||
},
|
||||
Etag: []byte("etag"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubProjectsAPI) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) {
|
||||
if a.setPolicyErr != nil {
|
||||
return nil, a.setPolicyErr
|
||||
}
|
||||
return &iampb.Policy{
|
||||
Version: 3,
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{
|
||||
"member",
|
||||
},
|
||||
},
|
||||
},
|
||||
Etag: []byte("etag"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubManagedInstanceIterator struct {
|
||||
instances []*computepb.ManagedInstance
|
||||
nextErr error
|
||||
|
||||
internalCounter int
|
||||
}
|
||||
|
||||
func (i *stubManagedInstanceIterator) Next() (*computepb.ManagedInstance, error) {
|
||||
if i.nextErr != nil {
|
||||
return nil, i.nextErr
|
||||
}
|
||||
if i.internalCounter >= len(i.instances) {
|
||||
i.internalCounter = 0
|
||||
return nil, iterator.Done
|
||||
}
|
||||
resp := i.instances[i.internalCounter]
|
||||
i.internalCounter++
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type stubAddressesAPI struct {
|
||||
insertErr error
|
||||
getAddr *string
|
||||
getErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (a stubAddressesAPI) Insert(context.Context, *computepb.InsertGlobalAddressRequest,
|
||||
...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.insertErr != nil {
|
||||
return nil, a.insertErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubAddressesAPI) Get(ctx context.Context, req *computepb.GetGlobalAddressRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Address, error) {
|
||||
return &computepb.Address{Address: a.getAddr}, a.getErr
|
||||
}
|
||||
|
||||
func (a stubAddressesAPI) Delete(context.Context, *computepb.DeleteGlobalAddressRequest,
|
||||
...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteErr != nil {
|
||||
return nil, a.deleteErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a stubAddressesAPI) Close() error {
|
||||
return nil
|
||||
}
|
@ -1,394 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
admin "cloud.google.com/go/iam/admin/apiv1"
|
||||
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"go.uber.org/multierr"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
// Client is a client for the Google Compute Engine.
|
||||
type Client struct {
|
||||
instanceAPI
|
||||
operationRegionAPI
|
||||
operationZoneAPI
|
||||
operationGlobalAPI
|
||||
networksAPI
|
||||
subnetworksAPI
|
||||
firewallsAPI
|
||||
forwardingRulesAPI
|
||||
backendServicesAPI
|
||||
healthChecksAPI
|
||||
targetTCPProxiesAPI
|
||||
instanceTemplateAPI
|
||||
instanceGroupManagersAPI
|
||||
iamAPI
|
||||
projectsAPI
|
||||
addressesAPI
|
||||
|
||||
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
|
||||
|
||||
// loadbalancer
|
||||
loadbalancerIP string
|
||||
loadbalancerIPname string
|
||||
loadbalancers []*loadBalancer
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
closers = append(closers, subnetAPI)
|
||||
forwardingRulesAPI, err := compute.NewGlobalForwardingRulesRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, forwardingRulesAPI)
|
||||
backendServicesAPI, err := compute.NewBackendServicesRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, backendServicesAPI)
|
||||
targetTCPProxiesAPI, err := compute.NewTargetTcpProxiesRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, targetTCPProxiesAPI)
|
||||
targetPoolsAPI, err := compute.NewTargetPoolsRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, targetPoolsAPI)
|
||||
healthChecksAPI, err := compute.NewHealthChecksRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, healthChecksAPI)
|
||||
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
|
||||
}
|
||||
closers = append(closers, projectsAPI)
|
||||
addressesAPI, err := compute.NewGlobalAddressesRESTClient(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},
|
||||
forwardingRulesAPI: &forwardingRulesClient{forwardingRulesAPI},
|
||||
backendServicesAPI: &backendServicesClient{backendServicesAPI},
|
||||
targetTCPProxiesAPI: &targetTCPProxiesClient{targetTCPProxiesAPI},
|
||||
healthChecksAPI: &healthChecksClient{healthChecksAPI},
|
||||
instanceTemplateAPI: &instanceTemplateClient{templAPI},
|
||||
instanceGroupManagersAPI: &instanceGroupManagersClient{groupAPI},
|
||||
iamAPI: &iamClient{iamAPI},
|
||||
projectsAPI: &projectsClient{projectsAPI},
|
||||
addressesAPI: &addressesClient{addressesAPI},
|
||||
workers: make(cloudtypes.Instances),
|
||||
controlPlanes: make(cloudtypes.Instances),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewInitialized creates an initialized client.
|
||||
func NewInitialized(ctx context.Context, project, zone, region, name string) (*Client, error) {
|
||||
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,
|
||||
c.operationRegionAPI,
|
||||
c.operationZoneAPI,
|
||||
c.operationGlobalAPI,
|
||||
c.networksAPI,
|
||||
c.subnetworksAPI,
|
||||
c.firewallsAPI,
|
||||
c.forwardingRulesAPI,
|
||||
c.backendServicesAPI,
|
||||
c.healthChecksAPI,
|
||||
c.instanceTemplateAPI,
|
||||
c.instanceGroupManagersAPI,
|
||||
c.iamAPI,
|
||||
c.projectsAPI,
|
||||
c.addressesAPI,
|
||||
}
|
||||
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.
|
||||
func (c *Client) GetState() state.ConstellationState {
|
||||
stat := state.ConstellationState{
|
||||
Name: c.name,
|
||||
UID: c.uid,
|
||||
CloudProvider: cloudprovider.GCP.String(),
|
||||
LoadBalancerIP: c.loadbalancerIP,
|
||||
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,
|
||||
GCPLoadbalancerIPname: c.loadbalancerIPname,
|
||||
}
|
||||
for _, lb := range c.loadbalancers {
|
||||
stat.GCPLoadbalancers = append(stat.GCPLoadbalancers, lb.name)
|
||||
}
|
||||
return stat
|
||||
}
|
||||
|
||||
// SetState sets the state of the client to the handed ConstellationState.
|
||||
func (c *Client) SetState(stat state.ConstellationState) {
|
||||
c.workers = stat.GCPWorkerInstances
|
||||
c.controlPlanes = stat.GCPControlPlaneInstances
|
||||
c.workerInstanceGroup = stat.GCPWorkerInstanceGroup
|
||||
c.controlPlaneInstanceGroup = stat.GCPControlPlaneInstanceGroup
|
||||
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
|
||||
c.workerTemplate = stat.GCPWorkerInstanceTemplate
|
||||
c.controlPlaneTemplate = stat.GCPControlPlaneInstanceTemplate
|
||||
c.loadbalancerIPname = stat.GCPLoadbalancerIPname
|
||||
c.loadbalancerIP = stat.LoadBalancerIP
|
||||
for _, lbName := range stat.GCPLoadbalancers {
|
||||
lb := &loadBalancer{
|
||||
name: lbName,
|
||||
hasForwardingRules: true,
|
||||
hasBackendService: true,
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
}
|
||||
c.loadbalancers = append(c.loadbalancers, lb)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// buildInstanceName returns a formatted name string.
|
||||
// The names are joined with a '-'.
|
||||
// If names is empty, the returned value is c.name + "-" + c.uid.
|
||||
func (c *Client) buildResourceName(names ...string) string {
|
||||
builder := strings.Builder{}
|
||||
|
||||
builder.WriteString(c.name)
|
||||
builder.WriteRune('-')
|
||||
for _, name := range names {
|
||||
builder.WriteString(name)
|
||||
builder.WriteRune('-')
|
||||
}
|
||||
builder.WriteString(c.uid)
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (c *Client) resourceURI(scope resourceScope, resourceType, resourceName string) string {
|
||||
const baseURI = "https://www.googleapis.com/compute/v1/projects/"
|
||||
|
||||
builder := strings.Builder{}
|
||||
|
||||
builder.WriteString(baseURI)
|
||||
builder.WriteString(c.project)
|
||||
|
||||
switch scope {
|
||||
case scopeGlobal:
|
||||
builder.WriteString("/global/")
|
||||
case scopeRegion:
|
||||
builder.WriteString("/regions/")
|
||||
builder.WriteString(c.region)
|
||||
builder.WriteRune('/')
|
||||
case scopeZone:
|
||||
builder.WriteString("/zones/")
|
||||
builder.WriteString(c.zone)
|
||||
builder.WriteRune('/')
|
||||
default:
|
||||
panic("unknown scope")
|
||||
}
|
||||
|
||||
builder.WriteString(resourceType)
|
||||
builder.WriteRune('/')
|
||||
builder.WriteString(resourceName)
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type resourceScope string
|
||||
|
||||
const (
|
||||
scopeGlobal resourceScope = "global"
|
||||
scopeRegion resourceScope = "region"
|
||||
scopeZone resourceScope = "zone"
|
||||
)
|
||||
|
||||
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.
|
||||
var err error
|
||||
for _, closer := range closers {
|
||||
err = multierr.Append(err, closer.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func isNotFoundError(err error) bool {
|
||||
var gAPIErr *googleapi.Error
|
||||
if !errors.As(err, &gAPIErr) {
|
||||
return false
|
||||
}
|
||||
return gAPIErr.Code == http.StatusNotFound
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m,
|
||||
// https://github.com/census-instrumentation/opencensus-go/issues/1262
|
||||
goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestSetGetState(t *testing.T) {
|
||||
state := state.ConstellationState{
|
||||
CloudProvider: cloudprovider.GCP.String(),
|
||||
GCPWorkerInstances: cloudtypes.Instances{
|
||||
"id-1": {
|
||||
PublicIP: "ip1",
|
||||
PrivateIP: "ip2",
|
||||
},
|
||||
},
|
||||
GCPControlPlaneInstances: cloudtypes.Instances{
|
||||
"id-1": {
|
||||
PublicIP: "ip3",
|
||||
PrivateIP: "ip4",
|
||||
},
|
||||
},
|
||||
GCPWorkerInstanceGroup: "group-id",
|
||||
GCPControlPlaneInstanceGroup: "group-id",
|
||||
GCPProject: "proj-id",
|
||||
GCPZone: "zone-id",
|
||||
GCPRegion: "region-id",
|
||||
Name: "name",
|
||||
UID: "uid",
|
||||
LoadBalancerIP: "ip5",
|
||||
GCPNetwork: "net-id",
|
||||
GCPSubnetwork: "subnet-id",
|
||||
GCPFirewalls: []string{"fw-1", "fw-2"},
|
||||
GCPWorkerInstanceTemplate: "temp-id",
|
||||
GCPControlPlaneInstanceTemplate: "temp-id",
|
||||
GCPLoadbalancers: []string{"lb-1", "lb-2"},
|
||||
}
|
||||
|
||||
t.Run("SetState", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
client := Client{}
|
||||
client.SetState(state)
|
||||
assert.Equal(state.GCPWorkerInstances, client.workers)
|
||||
assert.Equal(state.GCPControlPlaneInstances, client.controlPlanes)
|
||||
assert.Equal(state.GCPWorkerInstanceGroup, client.workerInstanceGroup)
|
||||
assert.Equal(state.GCPControlPlaneInstanceGroup, client.controlPlaneInstanceGroup)
|
||||
assert.Equal(state.GCPProject, client.project)
|
||||
assert.Equal(state.GCPZone, client.zone)
|
||||
assert.Equal(state.Name, client.name)
|
||||
assert.Equal(state.UID, client.uid)
|
||||
assert.Equal(state.GCPNetwork, client.network)
|
||||
assert.Equal(state.GCPFirewalls, client.firewalls)
|
||||
assert.Equal(state.GCPControlPlaneInstanceTemplate, client.controlPlaneTemplate)
|
||||
assert.Equal(state.GCPWorkerInstanceTemplate, client.workerTemplate)
|
||||
assert.Equal(state.LoadBalancerIP, client.loadbalancerIP)
|
||||
for _, lb := range client.loadbalancers {
|
||||
assert.Contains(state.GCPLoadbalancers, lb.name)
|
||||
assert.True(lb.hasBackendService)
|
||||
assert.True(lb.hasHealthCheck)
|
||||
assert.True(lb.hasForwardingRules)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetState", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
client := Client{
|
||||
workers: state.GCPWorkerInstances,
|
||||
controlPlanes: state.GCPControlPlaneInstances,
|
||||
workerInstanceGroup: state.GCPWorkerInstanceGroup,
|
||||
controlPlaneInstanceGroup: state.GCPControlPlaneInstanceGroup,
|
||||
project: state.GCPProject,
|
||||
zone: state.GCPZone,
|
||||
region: state.GCPRegion,
|
||||
name: state.Name,
|
||||
uid: state.UID,
|
||||
network: state.GCPNetwork,
|
||||
subnetwork: state.GCPSubnetwork,
|
||||
firewalls: state.GCPFirewalls,
|
||||
workerTemplate: state.GCPWorkerInstanceTemplate,
|
||||
controlPlaneTemplate: state.GCPControlPlaneInstanceTemplate,
|
||||
loadbalancerIP: state.LoadBalancerIP,
|
||||
loadbalancerIPname: state.GCPLoadbalancerIPname,
|
||||
}
|
||||
for _, lbName := range state.GCPLoadbalancers {
|
||||
client.loadbalancers = append(client.loadbalancers, &loadBalancer{name: lbName})
|
||||
}
|
||||
|
||||
stat := client.GetState()
|
||||
|
||||
assert.Equal(state, stat)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildResourceName(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
clientUID string
|
||||
clientName string
|
||||
names []string
|
||||
wantName string
|
||||
}{
|
||||
"no names": {
|
||||
clientUID: "uid",
|
||||
clientName: "name",
|
||||
wantName: "name-uid",
|
||||
},
|
||||
"one name": {
|
||||
clientUID: "uid",
|
||||
clientName: "name",
|
||||
names: []string{"foo"},
|
||||
wantName: "name-foo-uid",
|
||||
},
|
||||
"two names": {
|
||||
clientUID: "uid",
|
||||
clientName: "name",
|
||||
names: []string{"foo", "bar"},
|
||||
wantName: "name-foo-bar-uid",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
client := Client{
|
||||
name: tc.clientName,
|
||||
uid: tc.clientUID,
|
||||
}
|
||||
|
||||
name := client.buildResourceName(tc.names...)
|
||||
|
||||
assert.Equal(tc.wantName, name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceURI(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
scope resourceScope
|
||||
resourceType string
|
||||
resourceName string
|
||||
wantURI string
|
||||
}{
|
||||
"global resource": {
|
||||
scope: scopeGlobal,
|
||||
resourceType: "healthChecks",
|
||||
resourceName: "name",
|
||||
wantURI: "https://www.googleapis.com/compute/v1/projects/project/global/healthChecks/name",
|
||||
},
|
||||
"regional resource": {
|
||||
scope: scopeRegion,
|
||||
resourceType: "healthChecks",
|
||||
resourceName: "name",
|
||||
wantURI: "https://www.googleapis.com/compute/v1/projects/project/regions/region/healthChecks/name",
|
||||
},
|
||||
"zonal resource": {
|
||||
scope: scopeZone,
|
||||
resourceType: "instanceGroups",
|
||||
resourceName: "name",
|
||||
wantURI: "https://www.googleapis.com/compute/v1/projects/project/zones/zone/instanceGroups/name",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
region: "region",
|
||||
}
|
||||
|
||||
uri := client.resourceURI(tc.scope, tc.resourceType, tc.resourceName)
|
||||
|
||||
assert.Equal(tc.wantURI, uri)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{}
|
||||
require.NoError(client.init("project", "zone", "region", "name"))
|
||||
assert.Equal("project", client.project)
|
||||
assert.Equal("zone", client.zone)
|
||||
assert.Equal("region", client.region)
|
||||
assert.Equal("name", client.name)
|
||||
}
|
||||
|
||||
func TestCloseAll(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
closers := []closer{&someCloser{}, &someCloser{}, &someCloser{}}
|
||||
assert.NoError(closeAll(closers))
|
||||
for _, c := range closers {
|
||||
assert.True(c.(*someCloser).closed)
|
||||
}
|
||||
|
||||
someErr := errors.New("failed")
|
||||
closers = []closer{&someCloser{}, &someCloser{closeErr: someErr}, &someCloser{}}
|
||||
assert.Error(closeAll(closers))
|
||||
for _, c := range closers {
|
||||
assert.True(c.(*someCloser).closed)
|
||||
}
|
||||
}
|
||||
|
||||
type someCloser struct {
|
||||
closeErr error
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (c *someCloser) Close() error {
|
||||
c.closed = true
|
||||
return c.closeErr
|
||||
}
|
||||
|
||||
func TestIsNotFoundError(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
err error
|
||||
result bool
|
||||
}{
|
||||
"not found error": {err: &googleapi.Error{Code: http.StatusNotFound}, result: true},
|
||||
"nil error": {err: nil, result: false},
|
||||
"other error": {err: errors.New("failed"), result: false},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal(tc.result, isNotFoundError(tc.err))
|
||||
})
|
||||
}
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
admin "cloud.google.com/go/iam/admin/apiv1"
|
||||
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
adminpb "google.golang.org/genproto/googleapis/iam/admin/v1"
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
)
|
||||
|
||||
type instanceClient struct {
|
||||
*compute.InstancesClient
|
||||
}
|
||||
|
||||
func (c *instanceClient) Close() error {
|
||||
return c.InstancesClient.Close()
|
||||
}
|
||||
|
||||
func (c *instanceClient) List(ctx context.Context, req *computepb.ListInstancesRequest,
|
||||
opts ...gax.CallOption,
|
||||
) InstanceIterator {
|
||||
return c.InstancesClient.List(ctx, req)
|
||||
}
|
||||
|
||||
type firewallsClient struct {
|
||||
*compute.FirewallsClient
|
||||
}
|
||||
|
||||
func (c *firewallsClient) Close() error {
|
||||
return c.FirewallsClient.Close()
|
||||
}
|
||||
|
||||
func (c *firewallsClient) Delete(ctx context.Context, req *computepb.DeleteFirewallRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.FirewallsClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *firewallsClient) Insert(ctx context.Context, req *computepb.InsertFirewallRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.FirewallsClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
type forwardingRulesClient struct {
|
||||
*compute.GlobalForwardingRulesClient
|
||||
}
|
||||
|
||||
func (c *forwardingRulesClient) Close() error {
|
||||
return c.GlobalForwardingRulesClient.Close()
|
||||
}
|
||||
|
||||
func (c *forwardingRulesClient) Delete(ctx context.Context, req *computepb.DeleteGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.GlobalForwardingRulesClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *forwardingRulesClient) Insert(ctx context.Context, req *computepb.InsertGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.GlobalForwardingRulesClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *forwardingRulesClient) Get(ctx context.Context, req *computepb.GetGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.ForwardingRule, error) {
|
||||
return c.GlobalForwardingRulesClient.Get(ctx, req)
|
||||
}
|
||||
|
||||
func (c *forwardingRulesClient) SetLabels(ctx context.Context, req *computepb.SetLabelsGlobalForwardingRuleRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.GlobalForwardingRulesClient.SetLabels(ctx, req)
|
||||
}
|
||||
|
||||
type backendServicesClient struct {
|
||||
*compute.BackendServicesClient
|
||||
}
|
||||
|
||||
func (c *backendServicesClient) Close() error {
|
||||
return c.BackendServicesClient.Close()
|
||||
}
|
||||
|
||||
func (c *backendServicesClient) Insert(ctx context.Context, req *computepb.InsertBackendServiceRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.BackendServicesClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *backendServicesClient) Delete(ctx context.Context, req *computepb.DeleteBackendServiceRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.BackendServicesClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
type targetTCPProxiesClient struct {
|
||||
*compute.TargetTcpProxiesClient
|
||||
}
|
||||
|
||||
func (c *targetTCPProxiesClient) Close() error {
|
||||
return c.TargetTcpProxiesClient.Close()
|
||||
}
|
||||
|
||||
func (c *targetTCPProxiesClient) Delete(ctx context.Context, req *computepb.DeleteTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.TargetTcpProxiesClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *targetTCPProxiesClient) Insert(ctx context.Context, req *computepb.InsertTargetTcpProxyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.TargetTcpProxiesClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
type healthChecksClient struct {
|
||||
*compute.HealthChecksClient
|
||||
}
|
||||
|
||||
func (c *healthChecksClient) Close() error {
|
||||
return c.HealthChecksClient.Close()
|
||||
}
|
||||
|
||||
func (c *healthChecksClient) Delete(ctx context.Context, req *computepb.DeleteHealthCheckRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.HealthChecksClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *healthChecksClient) Insert(ctx context.Context, req *computepb.InsertHealthCheckRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.HealthChecksClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
type networksClient struct {
|
||||
*compute.NetworksClient
|
||||
}
|
||||
|
||||
func (c *networksClient) Close() error {
|
||||
return c.NetworksClient.Close()
|
||||
}
|
||||
|
||||
func (c *networksClient) Insert(ctx context.Context, req *computepb.InsertNetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.NetworksClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *networksClient) Delete(ctx context.Context, req *computepb.DeleteNetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.NetworksClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
type subnetworksClient struct {
|
||||
*compute.SubnetworksClient
|
||||
}
|
||||
|
||||
func (c *subnetworksClient) Close() error {
|
||||
return c.SubnetworksClient.Close()
|
||||
}
|
||||
|
||||
func (c *subnetworksClient) Insert(ctx context.Context, req *computepb.InsertSubnetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.SubnetworksClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *subnetworksClient) Delete(ctx context.Context, req *computepb.DeleteSubnetworkRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.SubnetworksClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
type instanceTemplateClient struct {
|
||||
*compute.InstanceTemplatesClient
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Close() error {
|
||||
return c.InstanceTemplatesClient.Close()
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceTemplatesClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceTemplatesClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
type instanceGroupManagersClient struct {
|
||||
*compute.InstanceGroupManagersClient
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) Close() error {
|
||||
return c.InstanceGroupManagersClient.Close()
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) Delete(ctx context.Context, req *computepb.DeleteInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceGroupManagersClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) Insert(ctx context.Context, req *computepb.InsertInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceGroupManagersClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) ListManagedInstances(ctx context.Context, req *computepb.ListManagedInstancesInstanceGroupManagersRequest,
|
||||
opts ...gax.CallOption,
|
||||
) ManagedInstanceIterator {
|
||||
return c.InstanceGroupManagersClient.ListManagedInstances(ctx, req)
|
||||
}
|
||||
|
||||
type iamClient struct {
|
||||
*admin.IamClient
|
||||
}
|
||||
|
||||
func (c *iamClient) Close() error {
|
||||
return c.IamClient.Close()
|
||||
}
|
||||
|
||||
func (c *iamClient) CreateServiceAccount(ctx context.Context, req *adminpb.CreateServiceAccountRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*adminpb.ServiceAccount, error) {
|
||||
return c.IamClient.CreateServiceAccount(ctx, req)
|
||||
}
|
||||
|
||||
func (c *iamClient) CreateServiceAccountKey(ctx context.Context, req *adminpb.CreateServiceAccountKeyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*adminpb.ServiceAccountKey, error) {
|
||||
return c.IamClient.CreateServiceAccountKey(ctx, req)
|
||||
}
|
||||
|
||||
func (c *iamClient) DeleteServiceAccount(ctx context.Context, req *adminpb.DeleteServiceAccountRequest,
|
||||
opts ...gax.CallOption,
|
||||
) error {
|
||||
return c.IamClient.DeleteServiceAccount(ctx, req)
|
||||
}
|
||||
|
||||
type projectsClient struct {
|
||||
*resourcemanager.ProjectsClient
|
||||
}
|
||||
|
||||
func (c *projectsClient) Close() error {
|
||||
return c.ProjectsClient.Close()
|
||||
}
|
||||
|
||||
func (c *projectsClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*iampb.Policy, error) {
|
||||
return c.ProjectsClient.GetIamPolicy(ctx, req)
|
||||
}
|
||||
|
||||
func (c *projectsClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*iampb.Policy, error) {
|
||||
return c.ProjectsClient.SetIamPolicy(ctx, req)
|
||||
}
|
||||
|
||||
type addressesClient struct {
|
||||
*compute.GlobalAddressesClient
|
||||
}
|
||||
|
||||
func (c *addressesClient) Insert(ctx context.Context, req *computepb.InsertGlobalAddressRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.GlobalAddressesClient.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *addressesClient) Delete(ctx context.Context, req *computepb.DeleteGlobalAddressRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.GlobalAddressesClient.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *addressesClient) Close() error {
|
||||
return c.GlobalAddressesClient.Close()
|
||||
}
|
@ -1,454 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/role"
|
||||
"google.golang.org/api/iterator"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// CreateInstances creates instances (virtual machines) on Google Compute Engine.
|
||||
//
|
||||
// A separate managed instance group is created for control planes and workers, the function
|
||||
// waits until the instances are up and stores the public and private IPs of the instances
|
||||
// in the client. If the client's network must be set before instances can be created.
|
||||
func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput) error {
|
||||
if c.network == "" {
|
||||
return errors.New("client has no network")
|
||||
}
|
||||
ops := []Operation{}
|
||||
|
||||
enableSerialConsole := strconv.FormatBool(input.EnableSerialConsole)
|
||||
|
||||
workerTemplateInput := insertInstanceTemplateInput{
|
||||
Name: c.buildResourceName("worker"),
|
||||
Network: c.network,
|
||||
SecondarySubnetworkRangeName: c.secondarySubnetworkRange,
|
||||
Subnetwork: c.subnetwork,
|
||||
EnableSerialConsole: enableSerialConsole,
|
||||
ImageID: input.ImageID,
|
||||
InstanceType: input.InstanceType,
|
||||
StateDiskSizeGB: int64(input.StateDiskSizeGB),
|
||||
StateDiskType: input.StateDiskType,
|
||||
Role: role.Worker.String(),
|
||||
KubeEnv: input.KubeEnv,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
Region: c.region,
|
||||
UID: c.uid,
|
||||
}
|
||||
op, err := c.insertInstanceTemplate(ctx, workerTemplateInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting instanceTemplate: %w", err)
|
||||
}
|
||||
ops = append(ops, op)
|
||||
c.workerTemplate = workerTemplateInput.Name
|
||||
|
||||
controlPlaneTemplateInput := insertInstanceTemplateInput{
|
||||
Name: c.buildResourceName("control-plane"),
|
||||
Network: c.network,
|
||||
Subnetwork: c.subnetwork,
|
||||
SecondarySubnetworkRangeName: c.secondarySubnetworkRange,
|
||||
EnableSerialConsole: enableSerialConsole,
|
||||
ImageID: input.ImageID,
|
||||
InstanceType: input.InstanceType,
|
||||
StateDiskSizeGB: int64(input.StateDiskSizeGB),
|
||||
StateDiskType: input.StateDiskType,
|
||||
Role: role.ControlPlane.String(),
|
||||
KubeEnv: input.KubeEnv,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
Region: c.region,
|
||||
UID: c.uid,
|
||||
}
|
||||
op, err = c.insertInstanceTemplate(ctx, controlPlaneTemplateInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting instanceTemplate: %w", err)
|
||||
}
|
||||
ops = append(ops, op)
|
||||
c.controlPlaneTemplate = controlPlaneTemplateInput.Name
|
||||
if err := c.waitForOperations(ctx, ops); err != nil {
|
||||
return err
|
||||
}
|
||||
ops = []Operation{}
|
||||
|
||||
controlPlaneGroupInput := instanceGroupManagerInput{
|
||||
Count: input.CountControlPlanes,
|
||||
Name: strings.Join([]string{c.name, "control-plane", c.uid}, "-"),
|
||||
NamedPorts: []*computepb.NamedPort{
|
||||
{Name: proto.String("kubernetes"), Port: proto.Int32(constants.KubernetesPort)},
|
||||
{Name: proto.String("debugd"), Port: proto.Int32(constants.DebugdPort)},
|
||||
{Name: proto.String("bootstrapper"), Port: proto.Int32(constants.BootstrapperPort)},
|
||||
{Name: proto.String("verify"), Port: proto.Int32(constants.VerifyServiceNodePortGRPC)},
|
||||
{Name: proto.String("konnectivity"), Port: proto.Int32(constants.KonnectivityPort)},
|
||||
{Name: proto.String("recovery"), Port: proto.Int32(constants.RecoveryPort)},
|
||||
},
|
||||
Template: c.controlPlaneTemplate,
|
||||
UID: c.uid,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
op, err = c.insertInstanceGroupManger(ctx, controlPlaneGroupInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting instanceGroupManager: %w", err)
|
||||
}
|
||||
ops = append(ops, op)
|
||||
c.controlPlaneInstanceGroup = controlPlaneGroupInput.Name
|
||||
|
||||
workerGroupInput := instanceGroupManagerInput{
|
||||
Count: input.CountWorkers,
|
||||
Name: strings.Join([]string{c.name, "worker", c.uid}, "-"),
|
||||
Template: c.workerTemplate,
|
||||
UID: c.uid,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
op, err = c.insertInstanceGroupManger(ctx, workerGroupInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting instanceGroupManager: %w", err)
|
||||
}
|
||||
ops = append(ops, op)
|
||||
c.workerInstanceGroup = workerGroupInput.Name
|
||||
|
||||
if err := c.waitForOperations(ctx, ops); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.waitForInstanceGroupScaling(ctx, c.workerInstanceGroup); err != nil {
|
||||
return fmt.Errorf("waiting for instanceGroupScaling: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForInstanceGroupScaling(ctx, c.controlPlaneInstanceGroup); err != nil {
|
||||
return fmt.Errorf("waiting for instanceGroupScaling: %w", err)
|
||||
}
|
||||
|
||||
if err := c.getInstanceIPs(ctx, c.workerInstanceGroup, c.workers); err != nil {
|
||||
return fmt.Errorf("getting instanceIPs: %w", err)
|
||||
}
|
||||
if err := c.getInstanceIPs(ctx, c.controlPlaneInstanceGroup, c.controlPlanes); err != nil {
|
||||
return fmt.Errorf("getting instanceIPs: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminateInstances terminates the clients instances.
|
||||
func (c *Client) TerminateInstances(ctx context.Context) error {
|
||||
ops := []Operation{}
|
||||
if c.workerInstanceGroup != "" {
|
||||
op, err := c.deleteInstanceGroupManager(ctx, c.workerInstanceGroup)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return fmt.Errorf("deleting instanceGroupManager '%s': %w", c.workerInstanceGroup, err)
|
||||
}
|
||||
if err == nil {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
c.workerInstanceGroup = ""
|
||||
c.workers = make(cloudtypes.Instances)
|
||||
}
|
||||
|
||||
if c.controlPlaneInstanceGroup != "" {
|
||||
op, err := c.deleteInstanceGroupManager(ctx, c.controlPlaneInstanceGroup)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return fmt.Errorf("deleting instanceGroupManager '%s': %w", c.controlPlaneInstanceGroup, err)
|
||||
}
|
||||
if err == nil {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
c.controlPlaneInstanceGroup = ""
|
||||
c.controlPlanes = make(cloudtypes.Instances)
|
||||
}
|
||||
if err := c.waitForOperations(ctx, ops); err != nil {
|
||||
return err
|
||||
}
|
||||
ops = []Operation{}
|
||||
|
||||
if c.workerTemplate != "" {
|
||||
op, err := c.deleteInstanceTemplate(ctx, c.workerTemplate)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return fmt.Errorf("deleting instanceTemplate: %w", err)
|
||||
}
|
||||
if err == nil {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
c.workerTemplate = ""
|
||||
}
|
||||
if c.controlPlaneTemplate != "" {
|
||||
op, err := c.deleteInstanceTemplate(ctx, c.controlPlaneTemplate)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return fmt.Errorf("deleting instanceTemplate: %w", err)
|
||||
}
|
||||
if err == nil {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
c.controlPlaneTemplate = ""
|
||||
}
|
||||
return c.waitForOperations(ctx, ops)
|
||||
}
|
||||
|
||||
func (c *Client) insertInstanceTemplate(ctx context.Context, input insertInstanceTemplateInput) (Operation, error) {
|
||||
req := input.insertInstanceTemplateRequest()
|
||||
return c.instanceTemplateAPI.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *Client) deleteInstanceTemplate(ctx context.Context, name string) (Operation, error) {
|
||||
req := &computepb.DeleteInstanceTemplateRequest{
|
||||
InstanceTemplate: name,
|
||||
Project: c.project,
|
||||
}
|
||||
return c.instanceTemplateAPI.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *Client) insertInstanceGroupManger(ctx context.Context, input instanceGroupManagerInput) (Operation, error) {
|
||||
req := input.InsertInstanceGroupManagerRequest()
|
||||
return c.instanceGroupManagersAPI.Insert(ctx, &req)
|
||||
}
|
||||
|
||||
func (c *Client) deleteInstanceGroupManager(ctx context.Context, instanceGroupManagerName string) (Operation, error) {
|
||||
req := &computepb.DeleteInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupManagerName,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
return c.instanceGroupManagersAPI.Delete(ctx, req)
|
||||
}
|
||||
|
||||
func (c *Client) waitForInstanceGroupScaling(ctx context.Context, groupID string) error {
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
listReq := &computepb.ListManagedInstancesInstanceGroupManagersRequest{
|
||||
InstanceGroupManager: groupID,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
it := c.instanceGroupManagersAPI.ListManagedInstances(ctx, listReq)
|
||||
for {
|
||||
resp, err := it.Next()
|
||||
if errors.Is(err, iterator.Done) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.CurrentAction == nil {
|
||||
return errors.New("currentAction is nil")
|
||||
}
|
||||
if *resp.CurrentAction != computepb.ManagedInstance_NONE.String() {
|
||||
time.Sleep(5 * time.Second)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getInstanceIPs requests the IPs of the client's instances.
|
||||
func (c *Client) getInstanceIPs(ctx context.Context, groupID string, list cloudtypes.Instances) error {
|
||||
req := &computepb.ListInstancesRequest{
|
||||
Filter: proto.String("name=" + groupID + "*"),
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
it := c.instanceAPI.List(ctx, req)
|
||||
for {
|
||||
resp, err := it.Next()
|
||||
if errors.Is(err, iterator.Done) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Name == nil {
|
||||
return errors.New("instance name is nil pointer")
|
||||
}
|
||||
if len(resp.NetworkInterfaces) == 0 {
|
||||
return errors.New("network interface is empty")
|
||||
}
|
||||
if resp.NetworkInterfaces[0].NetworkIP == nil {
|
||||
return errors.New("networkIP is nil")
|
||||
}
|
||||
if len(resp.NetworkInterfaces[0].AccessConfigs) == 0 {
|
||||
return errors.New("access configs is empty")
|
||||
}
|
||||
if resp.NetworkInterfaces[0].AccessConfigs[0].NatIP == nil {
|
||||
return errors.New("natIP is nil")
|
||||
}
|
||||
instance := cloudtypes.Instance{
|
||||
PrivateIP: *resp.NetworkInterfaces[0].NetworkIP,
|
||||
PublicIP: *resp.NetworkInterfaces[0].AccessConfigs[0].NatIP,
|
||||
}
|
||||
list[*resp.Name] = instance
|
||||
}
|
||||
}
|
||||
|
||||
type instanceGroupManagerInput struct {
|
||||
Count int
|
||||
Name string
|
||||
NamedPorts []*computepb.NamedPort
|
||||
Template string
|
||||
Project string
|
||||
Zone string
|
||||
UID string
|
||||
}
|
||||
|
||||
func (i *instanceGroupManagerInput) InsertInstanceGroupManagerRequest() computepb.InsertInstanceGroupManagerRequest {
|
||||
return computepb.InsertInstanceGroupManagerRequest{
|
||||
InstanceGroupManagerResource: &computepb.InstanceGroupManager{
|
||||
BaseInstanceName: proto.String(i.Name),
|
||||
NamedPorts: i.NamedPorts,
|
||||
InstanceTemplate: proto.String("projects/" + i.Project + "/global/instanceTemplates/" + i.Template),
|
||||
Name: proto.String(i.Name),
|
||||
TargetSize: proto.Int32(int32(i.Count)),
|
||||
},
|
||||
Project: i.Project,
|
||||
Zone: i.Zone,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateInstancesInput is the input for a CreatInstances operation.
|
||||
type CreateInstancesInput struct {
|
||||
EnableSerialConsole bool
|
||||
CountWorkers int
|
||||
CountControlPlanes int
|
||||
ImageID string
|
||||
InstanceType string
|
||||
StateDiskSizeGB int
|
||||
StateDiskType string
|
||||
KubeEnv string
|
||||
}
|
||||
|
||||
type insertInstanceTemplateInput struct {
|
||||
Name string
|
||||
Network string
|
||||
Subnetwork string
|
||||
SecondarySubnetworkRangeName string
|
||||
EnableSerialConsole string
|
||||
ImageID string
|
||||
InstanceType string
|
||||
StateDiskSizeGB int64
|
||||
StateDiskType string
|
||||
Role string
|
||||
KubeEnv string
|
||||
Project string
|
||||
Zone string
|
||||
Region string
|
||||
UID string
|
||||
}
|
||||
|
||||
func (i insertInstanceTemplateInput) insertInstanceTemplateRequest() *computepb.InsertInstanceTemplateRequest {
|
||||
req := computepb.InsertInstanceTemplateRequest{
|
||||
InstanceTemplateResource: &computepb.InstanceTemplate{
|
||||
Description: proto.String("This instance belongs to a Constellation cluster."),
|
||||
Name: proto.String(i.Name),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
ConfidentialInstanceConfig: &computepb.ConfidentialInstanceConfig{
|
||||
EnableConfidentialCompute: proto.Bool(true),
|
||||
},
|
||||
Description: proto.String("This instance belongs to a Constellation cluster."),
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
DiskSizeGb: proto.Int64(10),
|
||||
SourceImage: proto.String(i.ImageID),
|
||||
},
|
||||
AutoDelete: proto.Bool(true),
|
||||
Boot: proto.Bool(true),
|
||||
Mode: proto.String(computepb.AttachedDisk_READ_WRITE.String()),
|
||||
},
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
DiskSizeGb: proto.Int64(i.StateDiskSizeGB),
|
||||
DiskType: proto.String(i.StateDiskType),
|
||||
},
|
||||
AutoDelete: proto.Bool(true),
|
||||
DeviceName: proto.String("state-disk"),
|
||||
Mode: proto.String(computepb.AttachedDisk_READ_WRITE.String()),
|
||||
Type: proto.String(computepb.AttachedDisk_PERSISTENT.String()),
|
||||
},
|
||||
},
|
||||
MachineType: proto.String(i.InstanceType),
|
||||
Metadata: &computepb.Metadata{
|
||||
Items: []*computepb.Items{
|
||||
{
|
||||
Key: proto.String("kube-env"),
|
||||
Value: proto.String(i.KubeEnv),
|
||||
},
|
||||
{
|
||||
Key: proto.String("constellation-uid"),
|
||||
Value: proto.String(i.UID),
|
||||
},
|
||||
{
|
||||
Key: proto.String("constellation-role"),
|
||||
Value: proto.String(i.Role),
|
||||
},
|
||||
{
|
||||
Key: proto.String("serial-port-enable"),
|
||||
Value: proto.String(i.EnableSerialConsole),
|
||||
},
|
||||
},
|
||||
},
|
||||
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||
{
|
||||
Network: proto.String("projects/" + i.Project + "/global/networks/" + i.Network),
|
||||
Subnetwork: proto.String("regions/" + i.Region + "/subnetworks/" + i.Subnetwork),
|
||||
AccessConfigs: []*computepb.AccessConfig{
|
||||
{Type: proto.String(computepb.AccessConfig_ONE_TO_ONE_NAT.String())},
|
||||
},
|
||||
},
|
||||
},
|
||||
Scheduling: &computepb.Scheduling{
|
||||
OnHostMaintenance: proto.String(computepb.Scheduling_TERMINATE.String()),
|
||||
},
|
||||
ServiceAccounts: []*computepb.ServiceAccount{
|
||||
{
|
||||
Scopes: []string{
|
||||
"https://www.googleapis.com/auth/compute",
|
||||
"https://www.googleapis.com/auth/servicecontrol",
|
||||
"https://www.googleapis.com/auth/service.management.readonly",
|
||||
"https://www.googleapis.com/auth/devstorage.read_only",
|
||||
"https://www.googleapis.com/auth/logging.write",
|
||||
"https://www.googleapis.com/auth/monitoring.write",
|
||||
"https://www.googleapis.com/auth/trace.append",
|
||||
},
|
||||
},
|
||||
},
|
||||
ShieldedInstanceConfig: &computepb.ShieldedInstanceConfig{
|
||||
EnableIntegrityMonitoring: proto.Bool(true),
|
||||
EnableSecureBoot: proto.Bool(true),
|
||||
EnableVtpm: proto.Bool(true),
|
||||
},
|
||||
Tags: &computepb.Tags{
|
||||
Items: []string{"constellation-" + i.UID},
|
||||
},
|
||||
},
|
||||
},
|
||||
Project: i.Project,
|
||||
}
|
||||
|
||||
// if there is an secondary IP range defined, we use it as an alias IP range
|
||||
if i.SecondarySubnetworkRangeName != "" {
|
||||
req.InstanceTemplateResource.Properties.NetworkInterfaces[0].AliasIpRanges = []*computepb.AliasIpRange{
|
||||
{
|
||||
IpCidrRange: proto.String("/24"),
|
||||
SubnetworkRangeName: proto.String(i.SecondarySubnetworkRangeName),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &req
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/api/googleapi"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestCreateInstances(t *testing.T) {
|
||||
testInstances := []*computepb.Instance{
|
||||
{
|
||||
Name: proto.String("instance-name-1"),
|
||||
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||
{
|
||||
AccessConfigs: []*computepb.AccessConfig{
|
||||
{NatIP: proto.String("public-ip")},
|
||||
},
|
||||
NetworkIP: proto.String("private-ip"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: proto.String("instance-name-2"),
|
||||
NetworkInterfaces: []*computepb.NetworkInterface{
|
||||
{
|
||||
AccessConfigs: []*computepb.AccessConfig{
|
||||
{NatIP: proto.String("public-ip")},
|
||||
},
|
||||
NetworkIP: proto.String("private-ip"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
testManagedInstances := []*computepb.ManagedInstance{
|
||||
{CurrentAction: proto.String(computepb.ManagedInstance_NONE.String())},
|
||||
{CurrentAction: proto.String(computepb.ManagedInstance_NONE.String())},
|
||||
}
|
||||
testInput := CreateInstancesInput{
|
||||
CountControlPlanes: 3,
|
||||
CountWorkers: 4,
|
||||
ImageID: "img",
|
||||
InstanceType: "n2d-standard-4",
|
||||
KubeEnv: "kube-env",
|
||||
}
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
instanceAPI instanceAPI
|
||||
operationZoneAPI operationZoneAPI
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
instanceTemplateAPI instanceTemplateAPI
|
||||
instanceGroupManagersAPI instanceGroupManagersAPI
|
||||
input CreateInstancesInput
|
||||
network string
|
||||
wantErr bool
|
||||
}{
|
||||
"successful create": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
},
|
||||
"failed no network": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{waitErr: someErr},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
"failed wait zonal op": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{waitErr: someErr},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
"failed wait global op": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
"failed insert template": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{insertErr: someErr},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
input: testInput,
|
||||
network: "network",
|
||||
wantErr: true,
|
||||
},
|
||||
"failed insert instanceGroupManager": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{insertErr: someErr},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
"failed instanceGroupManager iterator": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{instances: testInstances}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{nextErr: someErr}},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
"failed instance iterator": {
|
||||
instanceAPI: stubInstanceAPI{listIterator: &stubInstanceIterator{nextErr: someErr}},
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{listIterator: &stubManagedInstanceIterator{instances: testManagedInstances}},
|
||||
network: "network",
|
||||
input: testInput,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
network: tc.network,
|
||||
subnetwork: "subnetwork",
|
||||
secondarySubnetworkRange: "secondary-range",
|
||||
instanceAPI: tc.instanceAPI,
|
||||
operationZoneAPI: tc.operationZoneAPI,
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
instanceTemplateAPI: tc.instanceTemplateAPI,
|
||||
instanceGroupManagersAPI: tc.instanceGroupManagersAPI,
|
||||
workers: make(cloudtypes.Instances),
|
||||
controlPlanes: make(cloudtypes.Instances),
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.CreateInstances(ctx, tc.input))
|
||||
} else {
|
||||
assert.NoError(client.CreateInstances(ctx, tc.input))
|
||||
assert.Equal([]string{"public-ip", "public-ip"}, client.workers.PublicIPs())
|
||||
assert.Equal([]string{"private-ip", "private-ip"}, client.workers.PrivateIPs())
|
||||
assert.Equal([]string{"public-ip", "public-ip"}, client.controlPlanes.PublicIPs())
|
||||
assert.Equal([]string{"private-ip", "private-ip"}, client.controlPlanes.PrivateIPs())
|
||||
assert.NotNil(client.workerInstanceGroup)
|
||||
assert.NotNil(client.controlPlaneInstanceGroup)
|
||||
assert.NotNil(client.controlPlaneTemplate)
|
||||
assert.NotNil(client.workerTemplate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminateInstances(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
notFoundErr := &googleapi.Error{Code: http.StatusNotFound}
|
||||
|
||||
testCases := map[string]struct {
|
||||
operationZoneAPI operationZoneAPI
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
instanceTemplateAPI instanceTemplateAPI
|
||||
instanceGroupManagersAPI instanceGroupManagersAPI
|
||||
|
||||
missingWorkerInstanceGroup bool
|
||||
wantErr bool
|
||||
}{
|
||||
"successful terminate": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{},
|
||||
},
|
||||
"successful terminate with missing worker instance group": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{},
|
||||
missingWorkerInstanceGroup: true,
|
||||
},
|
||||
"instances not found": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{deleteErr: notFoundErr},
|
||||
},
|
||||
"templates not found": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{deleteErr: notFoundErr},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{},
|
||||
},
|
||||
"fail delete instanceGroupManager": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{deleteErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"fail delete instanceTemplate": {
|
||||
operationZoneAPI: stubOperationZoneAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
instanceTemplateAPI: stubInstanceTemplateAPI{deleteErr: someErr},
|
||||
instanceGroupManagersAPI: stubInstanceGroupManagersAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
operationZoneAPI: tc.operationZoneAPI,
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
instanceTemplateAPI: tc.instanceTemplateAPI,
|
||||
instanceGroupManagersAPI: tc.instanceGroupManagersAPI,
|
||||
workers: cloudtypes.Instances{"worker-id-1": cloudtypes.Instance{}, "worker-id-2": cloudtypes.Instance{}},
|
||||
controlPlanes: cloudtypes.Instances{"controlplane-id-1": cloudtypes.Instance{}},
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
network: "network-id-1",
|
||||
workerInstanceGroup: "workerInstanceGroup-id-1",
|
||||
controlPlaneInstanceGroup: "controlplaneInstanceGroup-id-1",
|
||||
workerTemplate: "template-id-1",
|
||||
controlPlaneTemplate: "template-id-1",
|
||||
}
|
||||
if tc.missingWorkerInstanceGroup {
|
||||
client.workerInstanceGroup = ""
|
||||
client.workers = cloudtypes.Instances{}
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.TerminateInstances(ctx))
|
||||
} else {
|
||||
assert.NoError(client.TerminateInstances(ctx))
|
||||
assert.Nil(client.workers.PublicIPs())
|
||||
assert.Nil(client.workers.PrivateIPs())
|
||||
assert.Nil(client.controlPlanes.PublicIPs())
|
||||
assert.Nil(client.controlPlanes.PrivateIPs())
|
||||
assert.Empty(client.workerInstanceGroup)
|
||||
assert.Empty(client.controlPlaneInstanceGroup)
|
||||
assert.Empty(client.controlPlaneTemplate)
|
||||
assert.Empty(client.workerTemplate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,493 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"go.uber.org/multierr"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type loadBalancer struct {
|
||||
name string
|
||||
|
||||
// For creation.
|
||||
ip string
|
||||
frontendPort int
|
||||
backendPortName string
|
||||
healthCheck computepb.HealthCheck_Type
|
||||
label bool
|
||||
|
||||
// For resource management.
|
||||
hasHealthCheck bool
|
||||
hasBackendService bool
|
||||
hasForwardingRules bool
|
||||
hasTargetTCPProxy bool
|
||||
}
|
||||
|
||||
// CreateLoadBalancers creates all necessary load balancers.
|
||||
func (c *Client) CreateLoadBalancers(ctx context.Context, isDebugCluster bool) error {
|
||||
if err := c.createIPAddr(ctx); err != nil {
|
||||
return fmt.Errorf("creating load balancer IP address: %w", err)
|
||||
}
|
||||
|
||||
//
|
||||
// LoadBalancer definitions.
|
||||
//
|
||||
// LoadBalancers added here also need to be referenced in instances.go:*Client.CreateInstances
|
||||
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("kube"),
|
||||
ip: c.loadbalancerIP,
|
||||
frontendPort: constants.KubernetesPort,
|
||||
backendPortName: "kubernetes",
|
||||
healthCheck: computepb.HealthCheck_HTTPS,
|
||||
label: true, // Label, so bootstrapper can find kube-apiserver.
|
||||
})
|
||||
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("boot"),
|
||||
ip: c.loadbalancerIPname,
|
||||
frontendPort: constants.BootstrapperPort,
|
||||
backendPortName: "bootstrapper",
|
||||
healthCheck: computepb.HealthCheck_TCP,
|
||||
})
|
||||
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("verify"),
|
||||
ip: c.loadbalancerIPname,
|
||||
frontendPort: constants.VerifyServiceNodePortGRPC,
|
||||
backendPortName: "verify",
|
||||
healthCheck: computepb.HealthCheck_TCP,
|
||||
})
|
||||
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("konnectivity"),
|
||||
ip: c.loadbalancerIPname,
|
||||
frontendPort: constants.KonnectivityPort,
|
||||
backendPortName: "konnectivity",
|
||||
healthCheck: computepb.HealthCheck_TCP,
|
||||
})
|
||||
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("recovery"),
|
||||
ip: c.loadbalancerIPname,
|
||||
frontendPort: constants.RecoveryPort,
|
||||
backendPortName: "recovery",
|
||||
healthCheck: computepb.HealthCheck_TCP,
|
||||
})
|
||||
|
||||
// Only create when the debug cluster flag is set in the Constellation config
|
||||
if isDebugCluster {
|
||||
c.loadbalancers = append(c.loadbalancers, &loadBalancer{
|
||||
name: c.buildResourceName("debugd"),
|
||||
ip: c.loadbalancerIPname,
|
||||
frontendPort: constants.DebugdPort,
|
||||
backendPortName: "debugd",
|
||||
healthCheck: computepb.HealthCheck_TCP,
|
||||
})
|
||||
}
|
||||
|
||||
// Load balancer creation.
|
||||
|
||||
errC := make(chan error)
|
||||
createLB := func(ctx context.Context, lb *loadBalancer) {
|
||||
errC <- c.createLoadBalancer(ctx, lb)
|
||||
}
|
||||
|
||||
for _, lb := range c.loadbalancers {
|
||||
go createLB(ctx, lb)
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < len(c.loadbalancers); i++ {
|
||||
err = multierr.Append(err, <-errC)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// createLoadBalancer creates a load balancer.
|
||||
func (c *Client) createLoadBalancer(ctx context.Context, lb *loadBalancer) error {
|
||||
if err := c.createHealthCheck(ctx, lb); err != nil {
|
||||
return fmt.Errorf("creating health checks: %w", err)
|
||||
}
|
||||
if err := c.createBackendService(ctx, lb); err != nil {
|
||||
return fmt.Errorf("creating backend services: %w", err)
|
||||
}
|
||||
if err := c.createTargetTCPProxy(ctx, lb); err != nil {
|
||||
return fmt.Errorf("creating target TCP proxies: %w", err)
|
||||
}
|
||||
if err := c.createForwardingRules(ctx, lb); err != nil {
|
||||
return fmt.Errorf("creating forwarding rules: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createHealthCheck(ctx context.Context, lb *loadBalancer) error {
|
||||
req := &computepb.InsertHealthCheckRequest{
|
||||
Project: c.project,
|
||||
HealthCheckResource: &computepb.HealthCheck{
|
||||
Name: proto.String(lb.name),
|
||||
Type: proto.String(computepb.HealthCheck_Type_name[int32(lb.healthCheck)]),
|
||||
CheckIntervalSec: proto.Int32(1),
|
||||
TimeoutSec: proto.Int32(1),
|
||||
},
|
||||
}
|
||||
switch lb.healthCheck {
|
||||
case computepb.HealthCheck_HTTPS:
|
||||
req.HealthCheckResource.HttpsHealthCheck = newHealthCheckHTTPS(lb.frontendPort)
|
||||
case computepb.HealthCheck_TCP:
|
||||
req.HealthCheckResource.TcpHealthCheck = newHealthCheckTCP(lb.frontendPort)
|
||||
}
|
||||
resp, err := c.healthChecksAPI.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting health check: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return fmt.Errorf("waiting for health check creation: %w", err)
|
||||
}
|
||||
|
||||
lb.hasHealthCheck = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createBackendService(ctx context.Context, lb *loadBalancer) error {
|
||||
req := &computepb.InsertBackendServiceRequest{
|
||||
Project: c.project,
|
||||
BackendServiceResource: &computepb.BackendService{
|
||||
Name: proto.String(lb.name),
|
||||
Protocol: proto.String(computepb.BackendService_Protocol_name[int32(computepb.BackendService_TCP)]),
|
||||
LoadBalancingScheme: proto.String(computepb.BackendService_LoadBalancingScheme_name[int32(computepb.BackendService_EXTERNAL)]),
|
||||
HealthChecks: []string{c.resourceURI(scopeGlobal, "healthChecks", lb.name)},
|
||||
PortName: proto.String(lb.backendPortName),
|
||||
Backends: []*computepb.Backend{
|
||||
{
|
||||
BalancingMode: proto.String(computepb.Backend_BalancingMode_name[int32(computepb.Backend_UTILIZATION)]),
|
||||
Group: proto.String(c.resourceURI(scopeZone, "instanceGroups", c.controlPlaneInstanceGroup)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := c.backendServicesAPI.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting backend services: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return fmt.Errorf("waiting for backend services creation: %w", err)
|
||||
}
|
||||
|
||||
lb.hasBackendService = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createForwardingRules(ctx context.Context, lb *loadBalancer) error {
|
||||
req := &computepb.InsertGlobalForwardingRuleRequest{
|
||||
Project: c.project,
|
||||
ForwardingRuleResource: &computepb.ForwardingRule{
|
||||
Name: proto.String(lb.name),
|
||||
IPAddress: proto.String(c.resourceURI(scopeGlobal, "addresses", c.loadbalancerIPname)),
|
||||
IPProtocol: proto.String(computepb.ForwardingRule_IPProtocolEnum_name[int32(computepb.ForwardingRule_TCP)]),
|
||||
LoadBalancingScheme: proto.String(computepb.ForwardingRule_LoadBalancingScheme_name[int32(computepb.ForwardingRule_EXTERNAL)]),
|
||||
PortRange: proto.String(strconv.Itoa(lb.frontendPort)),
|
||||
|
||||
Target: proto.String(c.resourceURI(scopeGlobal, "targetTcpProxies", lb.name)),
|
||||
},
|
||||
}
|
||||
resp, err := c.forwardingRulesAPI.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting forwarding rules: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
lb.hasForwardingRules = true
|
||||
|
||||
if lb.label {
|
||||
return c.labelLoadBalancer(ctx, lb.name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// labelLoadBalancer labels a load balancer (its forwarding rules) so that it can be found by applications in the cluster.
|
||||
func (c *Client) labelLoadBalancer(ctx context.Context, name string) error {
|
||||
forwardingRule, err := c.forwardingRulesAPI.Get(ctx, &computepb.GetGlobalForwardingRuleRequest{
|
||||
Project: c.project,
|
||||
ForwardingRule: name,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting forwarding rule: %w", err)
|
||||
}
|
||||
if forwardingRule.LabelFingerprint == nil {
|
||||
return fmt.Errorf("forwarding rule %s has no label fingerprint", name)
|
||||
}
|
||||
|
||||
resp, err := c.forwardingRulesAPI.SetLabels(ctx, &computepb.SetLabelsGlobalForwardingRuleRequest{
|
||||
Project: c.project,
|
||||
Resource: name,
|
||||
GlobalSetLabelsRequestResource: &computepb.GlobalSetLabelsRequest{
|
||||
Labels: map[string]string{"constellation-uid": c.uid},
|
||||
LabelFingerprint: forwardingRule.LabelFingerprint,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting forwarding rule labels: %w", err)
|
||||
}
|
||||
|
||||
return c.waitForOperations(ctx, []Operation{resp})
|
||||
}
|
||||
|
||||
func (c *Client) createTargetTCPProxy(ctx context.Context, lb *loadBalancer) error {
|
||||
req := &computepb.InsertTargetTcpProxyRequest{
|
||||
Project: c.project,
|
||||
TargetTcpProxyResource: &computepb.TargetTcpProxy{
|
||||
Name: proto.String(lb.name),
|
||||
Service: proto.String(c.resourceURI(scopeGlobal, "backendServices", lb.name)),
|
||||
},
|
||||
}
|
||||
resp, err := c.targetTCPProxiesAPI.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting target tcp proxy: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
lb.hasTargetTCPProxy = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminateLoadBalancers terminates all load balancers.
|
||||
func (c *Client) TerminateLoadBalancers(ctx context.Context) error {
|
||||
errC := make(chan error)
|
||||
|
||||
terminateLB := func(ctx context.Context, lb *loadBalancer) {
|
||||
errC <- c.terminateLoadBalancer(ctx, lb)
|
||||
}
|
||||
|
||||
for _, lb := range c.loadbalancers {
|
||||
go terminateLB(ctx, lb)
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < len(c.loadbalancers); i++ {
|
||||
err = multierr.Append(err, <-errC)
|
||||
}
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.deleteIPAddr(ctx); err != nil {
|
||||
return fmt.Errorf("deleting load balancer IP address: %w", err)
|
||||
}
|
||||
|
||||
c.loadbalancers = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// terminateLoadBalancer removes the load balancer and its associated resources.
|
||||
func (c *Client) terminateLoadBalancer(ctx context.Context, lb *loadBalancer) error {
|
||||
if lb == nil {
|
||||
return nil
|
||||
}
|
||||
if lb.name == "" {
|
||||
return errors.New("load balancer name is empty")
|
||||
}
|
||||
|
||||
if lb.hasForwardingRules {
|
||||
if err := c.terminateForwadingRules(ctx, lb); err != nil {
|
||||
return fmt.Errorf("terminating forwarding rules: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if lb.hasTargetTCPProxy {
|
||||
if err := c.terminateTargetTCPProxy(ctx, lb); err != nil {
|
||||
return fmt.Errorf("terminating target tcp proxy: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if lb.hasBackendService {
|
||||
if err := c.terminateBackendService(ctx, lb); err != nil {
|
||||
return fmt.Errorf("terminating backend services: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if lb.hasHealthCheck {
|
||||
if err := c.terminateHealthCheck(ctx, lb); err != nil {
|
||||
return fmt.Errorf("terminating health checks: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
lb.name = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) terminateForwadingRules(ctx context.Context, lb *loadBalancer) error {
|
||||
resp, err := c.forwardingRulesAPI.Delete(ctx, &computepb.DeleteGlobalForwardingRuleRequest{
|
||||
Project: c.project,
|
||||
ForwardingRule: lb.name,
|
||||
})
|
||||
if isNotFoundError(err) {
|
||||
lb.hasForwardingRules = false
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting forwarding rules: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lb.hasForwardingRules = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) terminateTargetTCPProxy(ctx context.Context, lb *loadBalancer) error {
|
||||
resp, err := c.targetTCPProxiesAPI.Delete(ctx, &computepb.DeleteTargetTcpProxyRequest{
|
||||
Project: c.project,
|
||||
TargetTcpProxy: lb.name,
|
||||
})
|
||||
if isNotFoundError(err) {
|
||||
lb.hasTargetTCPProxy = false
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting target tcp proxy: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lb.hasTargetTCPProxy = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) terminateBackendService(ctx context.Context, lb *loadBalancer) error {
|
||||
resp, err := c.backendServicesAPI.Delete(ctx, &computepb.DeleteBackendServiceRequest{
|
||||
Project: c.project,
|
||||
BackendService: lb.name,
|
||||
})
|
||||
if isNotFoundError(err) {
|
||||
lb.hasBackendService = false
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting backend services: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lb.hasBackendService = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) terminateHealthCheck(ctx context.Context, lb *loadBalancer) error {
|
||||
resp, err := c.healthChecksAPI.Delete(ctx, &computepb.DeleteHealthCheckRequest{
|
||||
Project: c.project,
|
||||
HealthCheck: lb.name,
|
||||
})
|
||||
if isNotFoundError(err) {
|
||||
lb.hasHealthCheck = false
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting health checks: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{resp}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lb.hasHealthCheck = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createIPAddr(ctx context.Context) error {
|
||||
ipName := c.buildResourceName()
|
||||
insertReq := &computepb.InsertGlobalAddressRequest{
|
||||
Project: c.project,
|
||||
AddressResource: &computepb.Address{
|
||||
Name: proto.String(ipName),
|
||||
},
|
||||
}
|
||||
op, err := c.addressesAPI.Insert(ctx, insertReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting address: %w", err)
|
||||
}
|
||||
if err := c.waitForOperations(ctx, []Operation{op}); err != nil {
|
||||
return err
|
||||
}
|
||||
c.loadbalancerIPname = ipName
|
||||
|
||||
getReq := &computepb.GetGlobalAddressRequest{
|
||||
Project: c.project,
|
||||
Address: c.loadbalancerIPname,
|
||||
}
|
||||
addr, err := c.addressesAPI.Get(ctx, getReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting address: %w", err)
|
||||
}
|
||||
if addr.Address == nil {
|
||||
return fmt.Errorf("address response without address: %q", addr)
|
||||
}
|
||||
|
||||
c.loadbalancerIP = *addr.Address
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) deleteIPAddr(ctx context.Context) error {
|
||||
if c.loadbalancerIPname == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &computepb.DeleteGlobalAddressRequest{
|
||||
Project: c.project,
|
||||
Address: c.loadbalancerIPname,
|
||||
}
|
||||
op, err := c.addressesAPI.Delete(ctx, req)
|
||||
if isNotFoundError(err) {
|
||||
c.loadbalancerIPname = ""
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting address: %w", err)
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{op}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.loadbalancerIPname = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func newHealthCheckHTTPS(port int) *computepb.HTTPSHealthCheck {
|
||||
return &computepb.HTTPSHealthCheck{
|
||||
Host: proto.String(""),
|
||||
Port: proto.Int32(int32(port)),
|
||||
RequestPath: proto.String("/readyz"),
|
||||
}
|
||||
}
|
||||
|
||||
func newHealthCheckTCP(port int) *computepb.TCPHealthCheck {
|
||||
return &computepb.TCPHealthCheck{
|
||||
Port: proto.Int32(int32(port)),
|
||||
}
|
||||
}
|
@ -1,750 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestCreateLoadBalancers(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
forwardingRule := &compute.ForwardingRule{LabelFingerprint: proto.String("fingerprint")}
|
||||
|
||||
testCases := map[string]struct {
|
||||
addrAPI addressesAPI
|
||||
healthAPI healthChecksAPI
|
||||
backendAPI backendServicesAPI
|
||||
proxyAPI targetTCPProxiesAPI
|
||||
forwardAPI forwardingRulesAPI
|
||||
operationAPI operationGlobalAPI
|
||||
isDebugCluster bool
|
||||
wantErr bool
|
||||
}{
|
||||
"successful create": {
|
||||
addrAPI: &stubAddressesAPI{getAddr: proto.String("192.0.2.1")},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
proxyAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{forwardingRule: forwardingRule},
|
||||
operationAPI: stubOperationGlobalAPI{},
|
||||
},
|
||||
"successful create (debug cluster)": {
|
||||
addrAPI: &stubAddressesAPI{getAddr: proto.String("192.0.2.1")},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
proxyAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{forwardingRule: forwardingRule},
|
||||
operationAPI: stubOperationGlobalAPI{},
|
||||
isDebugCluster: true,
|
||||
},
|
||||
"createIPAddr fails": {
|
||||
addrAPI: &stubAddressesAPI{insertErr: someErr},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
proxyAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{forwardingRule: forwardingRule},
|
||||
operationAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"createLB fails": {
|
||||
addrAPI: &stubAddressesAPI{},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{insertErr: someErr},
|
||||
proxyAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{forwardingRule: forwardingRule},
|
||||
operationAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"createTcpProxy fails": {
|
||||
addrAPI: &stubAddressesAPI{getAddr: proto.String("192.0.2.1")},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
proxyAPI: &stubTargetTCPProxiesAPI{insertErr: someErr},
|
||||
forwardAPI: &stubForwardingRulesAPI{forwardingRule: forwardingRule},
|
||||
operationAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
addressesAPI: tc.addrAPI,
|
||||
targetTCPProxiesAPI: tc.proxyAPI,
|
||||
healthChecksAPI: tc.healthAPI,
|
||||
backendServicesAPI: tc.backendAPI,
|
||||
forwardingRulesAPI: tc.forwardAPI,
|
||||
operationGlobalAPI: tc.operationAPI,
|
||||
}
|
||||
|
||||
err := client.CreateLoadBalancers(ctx, tc.isDebugCluster)
|
||||
|
||||
// In case we expect an error, check for the error and continue otherwise.
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// If we don't expect an error, check if the resources have been successfully created.
|
||||
assert.NoError(err)
|
||||
assert.NotEmpty(client.loadbalancerIPname)
|
||||
|
||||
var foundDebugdLB bool
|
||||
for _, lb := range client.loadbalancers {
|
||||
// Expect load balancer name to have the format of "name-serviceName-uid" which is what buildResourceName does currently.
|
||||
if lb.name == fmt.Sprintf("%s-debugd-%s", client.name, client.uid) {
|
||||
foundDebugdLB = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if tc.isDebugCluster {
|
||||
assert.Equal(6, len(client.loadbalancers))
|
||||
assert.True(foundDebugdLB, "debugd loadbalancer not found in debug-mode")
|
||||
} else {
|
||||
assert.Equal(5, len(client.loadbalancers))
|
||||
assert.False(foundDebugdLB, "debugd loadbalancer found in non-debug mode")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
testCases := map[string]struct {
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
healthChecksAPI healthChecksAPI
|
||||
backendServicesAPI backendServicesAPI
|
||||
forwardingRulesAPI forwardingRulesAPI
|
||||
targetTCPProxiesAPI targetTCPProxiesAPI
|
||||
wantErr bool
|
||||
wantLB *loadBalancer
|
||||
}{
|
||||
"successful create": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{forwardingRule: &compute.ForwardingRule{LabelFingerprint: proto.String("fingerprint")}},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
},
|
||||
},
|
||||
"successful create with label": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{forwardingRule: &compute.ForwardingRule{LabelFingerprint: proto.String("fingerprint")}},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
label: true,
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when getting forwarding rule": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{getErr: someErr},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
label: true,
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when label fingerprint is missing": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{forwardingRule: &compute.ForwardingRule{}},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
label: true,
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when creating health check": {
|
||||
healthChecksAPI: stubHealthChecksAPI{insertErr: someErr},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{forwardingRule: &compute.ForwardingRule{LabelFingerprint: proto.String("fingerprint")}},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: false,
|
||||
hasTargetTCPProxy: false,
|
||||
hasBackendService: false,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when creating backend service": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{insertErr: someErr},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: false,
|
||||
hasTargetTCPProxy: false,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when creating forwarding rule": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{insertErr: someErr},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when creating target proxy rule": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{insertErr: someErr},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
},
|
||||
"CreateLoadBalancer fails when waiting on operation": {
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{forwardingRule: &compute.ForwardingRule{LabelFingerprint: proto.String("fingerprint")}},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
frontendPort: 1234,
|
||||
backendPortName: "testport",
|
||||
hasHealthCheck: false,
|
||||
hasBackendService: false,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
backendServicesAPI: tc.backendServicesAPI,
|
||||
forwardingRulesAPI: tc.forwardingRulesAPI,
|
||||
targetTCPProxiesAPI: tc.targetTCPProxiesAPI,
|
||||
healthChecksAPI: tc.healthChecksAPI,
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
}
|
||||
lb := &loadBalancer{
|
||||
name: tc.wantLB.name,
|
||||
frontendPort: tc.wantLB.frontendPort,
|
||||
backendPortName: tc.wantLB.backendPortName,
|
||||
label: tc.wantLB.label,
|
||||
}
|
||||
|
||||
err := client.createLoadBalancer(ctx, lb)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Equal(tc.wantLB, lb)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantLB, lb)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminateLoadbalancers(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
newRunningLB := func() *loadBalancer {
|
||||
return &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasForwardingRules: true,
|
||||
}
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
addrAPI addressesAPI
|
||||
healthAPI healthChecksAPI
|
||||
backendAPI backendServicesAPI
|
||||
targetAPI targetTCPProxiesAPI
|
||||
forwardAPI forwardingRulesAPI
|
||||
opGlobalAPI operationGlobalAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"successful terminate": {
|
||||
addrAPI: &stubAddressesAPI{},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
targetAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
},
|
||||
"deleteIPAddr fails": {
|
||||
addrAPI: &stubAddressesAPI{deleteErr: someErr},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{},
|
||||
targetAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"deleteLB fails": {
|
||||
addrAPI: &stubAddressesAPI{},
|
||||
healthAPI: &stubHealthChecksAPI{},
|
||||
backendAPI: &stubBackendServicesAPI{deleteErr: someErr},
|
||||
targetAPI: &stubTargetTCPProxiesAPI{},
|
||||
forwardAPI: &stubForwardingRulesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
addressesAPI: tc.addrAPI,
|
||||
healthChecksAPI: tc.healthAPI,
|
||||
backendServicesAPI: tc.backendAPI,
|
||||
targetTCPProxiesAPI: tc.targetAPI,
|
||||
forwardingRulesAPI: tc.forwardAPI,
|
||||
operationGlobalAPI: tc.opGlobalAPI,
|
||||
loadbalancerIPname: "loadbalancerIPid",
|
||||
loadbalancers: []*loadBalancer{
|
||||
newRunningLB(),
|
||||
newRunningLB(),
|
||||
newRunningLB(),
|
||||
},
|
||||
}
|
||||
|
||||
err := client.TerminateLoadBalancers(ctx)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Empty(client.loadbalancerIPname)
|
||||
assert.Nil(client.loadbalancers)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminateLoadBalancer(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
notFoundErr := &googleapi.Error{Code: http.StatusNotFound}
|
||||
newRunningLB := func() *loadBalancer {
|
||||
return &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasTargetTCPProxy: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
}
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
lb *loadBalancer
|
||||
opGlobalAPI operationGlobalAPI
|
||||
healthChecksAPI healthChecksAPI
|
||||
backendServicesAPI backendServicesAPI
|
||||
targetTCPProxiesAPI targetTCPProxiesAPI
|
||||
forwardingRulesAPI forwardingRulesAPI
|
||||
wantErr bool
|
||||
wantLB *loadBalancer
|
||||
}{
|
||||
"successful terminate": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"terminate partially created loadbalancer": {
|
||||
lb: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{deleteErr: someErr},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"terminate partially created loadbalancer 2": {
|
||||
lb: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: false,
|
||||
hasForwardingRules: false,
|
||||
},
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{deleteErr: someErr},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{deleteErr: someErr},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"no-op for nil loadbalancer": {
|
||||
lb: nil,
|
||||
},
|
||||
"health check not found": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{deleteErr: notFoundErr},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"backend service not found": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{deleteErr: notFoundErr},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"forwarding rules not found": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{deleteErr: notFoundErr},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"fails for loadbalancer without name": {
|
||||
lb: &loadBalancer{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{},
|
||||
},
|
||||
"fails when deleting health check": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{deleteErr: someErr},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: false,
|
||||
hasForwardingRules: false,
|
||||
hasTargetTCPProxy: false,
|
||||
},
|
||||
},
|
||||
"fails when deleting backend service": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{deleteErr: someErr},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: false,
|
||||
hasTargetTCPProxy: false,
|
||||
},
|
||||
},
|
||||
"fails when deleting forwarding rule": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{deleteErr: someErr},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
hasTargetTCPProxy: true,
|
||||
},
|
||||
},
|
||||
"fails when deleting tcp proxy rule": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{deleteErr: someErr},
|
||||
opGlobalAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: false,
|
||||
hasTargetTCPProxy: true,
|
||||
},
|
||||
},
|
||||
"fails when waiting on operation": {
|
||||
lb: newRunningLB(),
|
||||
healthChecksAPI: stubHealthChecksAPI{},
|
||||
backendServicesAPI: stubBackendServicesAPI{},
|
||||
forwardingRulesAPI: stubForwardingRulesAPI{},
|
||||
targetTCPProxiesAPI: stubTargetTCPProxiesAPI{},
|
||||
opGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
wantErr: true,
|
||||
wantLB: &loadBalancer{
|
||||
name: "name",
|
||||
hasHealthCheck: true,
|
||||
hasBackendService: true,
|
||||
hasForwardingRules: true,
|
||||
hasTargetTCPProxy: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
backendServicesAPI: tc.backendServicesAPI,
|
||||
forwardingRulesAPI: tc.forwardingRulesAPI,
|
||||
healthChecksAPI: tc.healthChecksAPI,
|
||||
targetTCPProxiesAPI: tc.targetTCPProxiesAPI,
|
||||
operationGlobalAPI: tc.opGlobalAPI,
|
||||
}
|
||||
|
||||
err := client.terminateLoadBalancer(ctx, tc.lb)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Equal(tc.wantLB, tc.lb)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantLB, tc.lb)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateIPAddr(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
addrAPI addressesAPI
|
||||
opAPI operationGlobalAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"successful create": {
|
||||
addrAPI: stubAddressesAPI{getAddr: proto.String("test-ip")},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
},
|
||||
"insert fails": {
|
||||
addrAPI: stubAddressesAPI{insertErr: someErr},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"get fails": {
|
||||
addrAPI: stubAddressesAPI{getErr: someErr},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"get address nil": {
|
||||
addrAPI: stubAddressesAPI{getAddr: nil},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"wait fails": {
|
||||
addrAPI: stubAddressesAPI{},
|
||||
opAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
addressesAPI: tc.addrAPI,
|
||||
operationGlobalAPI: tc.opAPI,
|
||||
}
|
||||
|
||||
err := client.createIPAddr(ctx)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal("test-ip", client.loadbalancerIP)
|
||||
assert.Equal("name-uid", client.loadbalancerIPname)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteIPAddr(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
notFoundErr := &googleapi.Error{Code: http.StatusNotFound}
|
||||
|
||||
testCases := map[string]struct {
|
||||
addrAPI addressesAPI
|
||||
opAPI operationGlobalAPI
|
||||
addrID string
|
||||
wantErr bool
|
||||
}{
|
||||
"successful delete": {
|
||||
addrAPI: stubAddressesAPI{},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
addrID: "name",
|
||||
},
|
||||
"not found": {
|
||||
addrAPI: stubAddressesAPI{deleteErr: notFoundErr},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
addrID: "name",
|
||||
},
|
||||
"empty is no-op": {
|
||||
addrAPI: stubAddressesAPI{},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
},
|
||||
"delete fails": {
|
||||
addrAPI: stubAddressesAPI{deleteErr: someErr},
|
||||
opAPI: stubOperationGlobalAPI{},
|
||||
addrID: "name",
|
||||
wantErr: true,
|
||||
},
|
||||
"wait fails": {
|
||||
addrAPI: stubAddressesAPI{},
|
||||
opAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
addrID: "name",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
addressesAPI: tc.addrAPI,
|
||||
operationGlobalAPI: tc.opAPI,
|
||||
loadbalancerIPname: tc.addrID,
|
||||
}
|
||||
|
||||
err := client.deleteIPAddr(ctx)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Empty(client.loadbalancerIPname)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
SubnetCIDR = "192.168.178.0/24"
|
||||
SubnetExtCIDR = "10.10.0.0/16"
|
||||
)
|
||||
|
||||
// CreateFirewall creates a set of firewall rules for the client's network.
|
||||
//
|
||||
// The client must have a VPC network to set firewall rules.
|
||||
func (c *Client) CreateFirewall(ctx context.Context, input FirewallInput) error {
|
||||
if c.network == "" {
|
||||
return errors.New("client has not network")
|
||||
}
|
||||
firewallRules, err := input.Ingress.GCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ops []Operation
|
||||
for _, rule := range firewallRules {
|
||||
c.firewalls = append(c.firewalls, rule.GetName())
|
||||
|
||||
rule.Network = proto.String("global/networks/" + c.network)
|
||||
rule.Name = proto.String(rule.GetName() + "-" + c.uid)
|
||||
req := &computepb.InsertFirewallRequest{
|
||||
FirewallResource: rule,
|
||||
Project: c.project,
|
||||
}
|
||||
resp, err := c.firewallsAPI.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Proto().Name == nil {
|
||||
return errors.New("operation name is nil")
|
||||
}
|
||||
ops = append(ops, resp)
|
||||
}
|
||||
return c.waitForOperations(ctx, ops)
|
||||
}
|
||||
|
||||
// TerminateFirewall deletes firewall rules from the client's network.
|
||||
//
|
||||
// The client must have a VPC network to set firewall rules.
|
||||
func (c *Client) TerminateFirewall(ctx context.Context) error {
|
||||
if len(c.firewalls) == 0 {
|
||||
return nil
|
||||
}
|
||||
var ops []Operation
|
||||
for _, name := range c.firewalls {
|
||||
ruleName := name + "-" + c.uid
|
||||
req := &computepb.DeleteFirewallRequest{
|
||||
Firewall: ruleName,
|
||||
Project: c.project,
|
||||
}
|
||||
resp, err := c.firewallsAPI.Delete(ctx, req)
|
||||
if isNotFoundError(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Proto().Name == nil {
|
||||
return errors.New("operation name is nil")
|
||||
}
|
||||
ops = append(ops, resp)
|
||||
}
|
||||
if err := c.waitForOperations(ctx, ops); err != nil {
|
||||
return err
|
||||
}
|
||||
c.firewalls = []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirewallInput defines firewall rules to be set.
|
||||
type FirewallInput struct {
|
||||
Ingress cloudtypes.Firewall
|
||||
Egress cloudtypes.Firewall
|
||||
}
|
||||
|
||||
// CreateVPCs creates all necessary VPC networks.
|
||||
func (c *Client) CreateVPCs(ctx context.Context) error {
|
||||
c.network = c.buildResourceName()
|
||||
|
||||
op, err := c.createVPC(ctx, c.network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.waitForOperations(ctx, []Operation{op}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.createSubnets(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createVPC creates a VPC network.
|
||||
func (c *Client) createVPC(ctx context.Context, name string) (Operation, error) {
|
||||
req := &computepb.InsertNetworkRequest{
|
||||
NetworkResource: &computepb.Network{
|
||||
AutoCreateSubnetworks: proto.Bool(false),
|
||||
Description: proto.String("Constellation VPC"),
|
||||
Name: proto.String(name),
|
||||
},
|
||||
Project: c.project,
|
||||
}
|
||||
return c.networksAPI.Insert(ctx, req)
|
||||
}
|
||||
|
||||
// TerminateVPCs terminates all VPC networks.
|
||||
//
|
||||
// If the any network has firewall rules, these must be terminated first.
|
||||
func (c *Client) TerminateVPCs(ctx context.Context) error {
|
||||
if len(c.firewalls) != 0 {
|
||||
return errors.New("client has firewalls, which must be deleted first")
|
||||
}
|
||||
|
||||
if err := c.terminateSubnet(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.terminateVPC(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// terminateVPC terminates a VPC network.
|
||||
//
|
||||
// If the network has firewall rules, these must be terminated first.
|
||||
func (c *Client) terminateVPC(ctx context.Context) error {
|
||||
if c.network == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &computepb.DeleteNetworkRequest{
|
||||
Project: c.project,
|
||||
Network: c.network,
|
||||
}
|
||||
op, err := c.networksAPI.Delete(ctx, req)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return err
|
||||
}
|
||||
if isNotFoundError(err) {
|
||||
c.network = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{op}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.network = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createSubnets(ctx context.Context) error {
|
||||
c.subnetwork = "node-net-" + c.uid
|
||||
c.secondarySubnetworkRange = "net-ext" + c.uid
|
||||
|
||||
op, err := c.createSubnet(ctx, c.subnetwork, c.network, c.secondarySubnetworkRange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.waitForOperations(ctx, []Operation{op})
|
||||
}
|
||||
|
||||
func (c *Client) createSubnet(ctx context.Context, name, network, secondaryRangeName string) (Operation, error) {
|
||||
req := &computepb.InsertSubnetworkRequest{
|
||||
Project: c.project,
|
||||
Region: c.region,
|
||||
SubnetworkResource: &computepb.Subnetwork{
|
||||
IpCidrRange: proto.String(SubnetCIDR),
|
||||
Name: proto.String(name),
|
||||
Network: proto.String("projects/" + c.project + "/global/networks/" + network),
|
||||
SecondaryIpRanges: []*computepb.SubnetworkSecondaryRange{
|
||||
{
|
||||
RangeName: proto.String(secondaryRangeName),
|
||||
IpCidrRange: proto.String(SubnetExtCIDR),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return c.subnetworksAPI.Insert(ctx, req)
|
||||
}
|
||||
|
||||
func (c *Client) terminateSubnet(ctx context.Context) error {
|
||||
if c.subnetwork == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &computepb.DeleteSubnetworkRequest{
|
||||
Project: c.project,
|
||||
Region: c.region,
|
||||
Subnetwork: c.subnetwork,
|
||||
}
|
||||
op, err := c.subnetworksAPI.Delete(ctx, req)
|
||||
if err != nil && !isNotFoundError(err) {
|
||||
return err
|
||||
}
|
||||
if isNotFoundError(err) {
|
||||
c.subnetwork = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.waitForOperations(ctx, []Operation{op}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.subnetwork = ""
|
||||
return nil
|
||||
}
|
@ -1,357 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
func TestCreateVPCs(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
operationRegionAPI operationRegionAPI
|
||||
networksAPI networksAPI
|
||||
subnetworksAPI subnetworksAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"successful create": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
},
|
||||
"failed wait global op": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"failed wait region op": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{waitErr: someErr},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"failed insert networks": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{insertErr: someErr},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"failed insert subnetworks": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{insertErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
operationRegionAPI: tc.operationRegionAPI,
|
||||
networksAPI: tc.networksAPI,
|
||||
subnetworksAPI: tc.subnetworksAPI,
|
||||
workers: make(cloudtypes.Instances),
|
||||
controlPlanes: make(cloudtypes.Instances),
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.CreateVPCs(ctx))
|
||||
} else {
|
||||
assert.NoError(client.CreateVPCs(ctx))
|
||||
assert.NotNil(client.network)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminateVPCs(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
notFoundErr := &googleapi.Error{Code: http.StatusNotFound}
|
||||
|
||||
testCases := map[string]struct {
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
operationRegionAPI operationRegionAPI
|
||||
networksAPI networksAPI
|
||||
subnetworksAPI subnetworksAPI
|
||||
firewalls []string
|
||||
subnetwork string
|
||||
network string
|
||||
wantErr bool
|
||||
}{
|
||||
"successful terminate": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
},
|
||||
"subnetwork empty": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "",
|
||||
network: "network-id-1",
|
||||
},
|
||||
"network empty": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "",
|
||||
},
|
||||
"subnetwork not found": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{deleteErr: notFoundErr},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
},
|
||||
"network not found": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{deleteErr: notFoundErr},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
},
|
||||
"failed wait global op": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
wantErr: true,
|
||||
},
|
||||
"failed delete networks": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{deleteErr: someErr},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
wantErr: true,
|
||||
},
|
||||
"failed delete subnetworks": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{deleteErr: someErr},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
wantErr: true,
|
||||
},
|
||||
"must delete firewalls first": {
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
operationRegionAPI: stubOperationRegionAPI{},
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
networksAPI: stubNetworksAPI{},
|
||||
subnetworksAPI: stubSubnetworksAPI{},
|
||||
subnetwork: "subnetwork-id-1",
|
||||
network: "network-id-1",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
operationRegionAPI: tc.operationRegionAPI,
|
||||
networksAPI: tc.networksAPI,
|
||||
subnetworksAPI: tc.subnetworksAPI,
|
||||
firewalls: tc.firewalls,
|
||||
network: tc.network,
|
||||
subnetwork: tc.subnetwork,
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.TerminateVPCs(ctx))
|
||||
} else {
|
||||
assert.NoError(client.TerminateVPCs(ctx))
|
||||
assert.Empty(client.network)
|
||||
assert.Empty(client.subnetwork)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFirewall(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
testFirewallInput := FirewallInput{
|
||||
Ingress: cloudtypes.Firewall{
|
||||
cloudtypes.FirewallRule{
|
||||
Name: "test-1",
|
||||
Description: "test-1 description",
|
||||
Protocol: "tcp",
|
||||
IPRange: "192.0.2.0/24",
|
||||
FromPort: 9000,
|
||||
},
|
||||
cloudtypes.FirewallRule{
|
||||
Name: "test-2",
|
||||
Description: "test-2 description",
|
||||
Protocol: "udp",
|
||||
IPRange: "192.0.2.0/24",
|
||||
FromPort: 51820,
|
||||
},
|
||||
},
|
||||
Egress: cloudtypes.Firewall{},
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
network string
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
firewallsAPI firewallsAPI
|
||||
firewallInput FirewallInput
|
||||
wantErr bool
|
||||
}{
|
||||
"successful create": {
|
||||
network: "network",
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
},
|
||||
"failed wait global op": {
|
||||
network: "network",
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
"failed insert networks": {
|
||||
network: "network",
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{insertErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"no network set": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
network: tc.network,
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
firewallsAPI: tc.firewallsAPI,
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.CreateFirewall(ctx, testFirewallInput))
|
||||
} else {
|
||||
assert.NoError(client.CreateFirewall(ctx, testFirewallInput))
|
||||
assert.ElementsMatch([]string{"test-1", "test-2"}, client.firewalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminateFirewall(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
notFoundErr := &googleapi.Error{Code: http.StatusNotFound}
|
||||
|
||||
testCases := map[string]struct {
|
||||
operationGlobalAPI operationGlobalAPI
|
||||
firewallsAPI firewallsAPI
|
||||
firewalls []string
|
||||
wantErr bool
|
||||
}{
|
||||
"successful terminate": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
},
|
||||
"successful terminate when no firewall exists": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
firewalls: []string{},
|
||||
},
|
||||
"successful terminate when firewall not found": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{deleteErr: notFoundErr},
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
},
|
||||
"failed to wait on global operation": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{waitErr: someErr},
|
||||
firewallsAPI: stubFirewallsAPI{},
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
wantErr: true,
|
||||
},
|
||||
"failed to delete firewalls": {
|
||||
operationGlobalAPI: stubOperationGlobalAPI{},
|
||||
firewallsAPI: stubFirewallsAPI{deleteErr: someErr},
|
||||
firewalls: []string{"firewall-1", "firewall-2"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
firewalls: tc.firewalls,
|
||||
operationGlobalAPI: tc.operationGlobalAPI,
|
||||
firewallsAPI: tc.firewallsAPI,
|
||||
}
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(client.TerminateFirewall(ctx))
|
||||
} else {
|
||||
assert.NoError(client.TerminateFirewall(ctx))
|
||||
assert.Empty(client.firewalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
// waitForOperations waits until every operation in the opIDs slice is
|
||||
// done or returns the first occurring error.
|
||||
func (c *Client) waitForOperations(ctx context.Context, ops []Operation) error {
|
||||
for _, op := range ops {
|
||||
switch {
|
||||
case op.Proto() == nil:
|
||||
return errors.New("proto of operation is nil")
|
||||
case op.Proto().Zone != nil:
|
||||
if err := c.waitForZoneOperation(ctx, op); err != nil {
|
||||
return err
|
||||
}
|
||||
case op.Proto().Region != nil:
|
||||
if err := c.waitForRegionOperation(ctx, op); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := c.waitForGlobalOperation(ctx, op); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) waitForGlobalOperation(ctx context.Context, op Operation) error {
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if op.Proto().Name == nil {
|
||||
return errors.New("operation name is nil")
|
||||
}
|
||||
waitReq := &computepb.WaitGlobalOperationRequest{
|
||||
Operation: *op.Proto().Name,
|
||||
Project: c.project,
|
||||
}
|
||||
zoneOp, err := c.operationGlobalAPI.Wait(ctx, waitReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to wait for the operation: %w", err)
|
||||
}
|
||||
if *zoneOp.Status.Enum() == computepb.Operation_DONE {
|
||||
if opErr := zoneOp.Error; opErr != nil {
|
||||
return fmt.Errorf("operation failed: %s", opErr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) waitForZoneOperation(ctx context.Context, op Operation) error {
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if op.Proto().Name == nil {
|
||||
return errors.New("operation name is nil")
|
||||
}
|
||||
waitReq := &computepb.WaitZoneOperationRequest{
|
||||
Operation: *op.Proto().Name,
|
||||
Project: c.project,
|
||||
Zone: c.zone,
|
||||
}
|
||||
zoneOp, err := c.operationZoneAPI.Wait(ctx, waitReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to wait for the operation: %w", err)
|
||||
}
|
||||
if *zoneOp.Status.Enum() == computepb.Operation_DONE {
|
||||
if opErr := zoneOp.Error; opErr != nil {
|
||||
return fmt.Errorf("operation failed: %s", opErr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) waitForRegionOperation(ctx context.Context, op Operation) error {
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
if op.Proto().Name == nil {
|
||||
return errors.New("operation name is nil")
|
||||
}
|
||||
waitReq := &computepb.WaitRegionOperationRequest{
|
||||
Operation: *op.Proto().Name,
|
||||
Project: c.project,
|
||||
Region: c.region,
|
||||
}
|
||||
regionOp, err := c.operationRegionAPI.Wait(ctx, waitReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to wait for the operation: %w", err)
|
||||
}
|
||||
if *regionOp.Status.Enum() == computepb.Operation_DONE {
|
||||
if opErr := regionOp.Error; opErr != nil {
|
||||
return fmt.Errorf("operation failed: %s", opErr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
)
|
||||
|
||||
// addIAMPolicyBindings adds a GCP service account to roles specified in the input.
|
||||
func (c *Client) addIAMPolicyBindings(ctx context.Context, input AddIAMPolicyBindingInput) error {
|
||||
getReq := &iampb.GetIamPolicyRequest{
|
||||
Resource: "projects/" + c.project,
|
||||
}
|
||||
policy, err := c.projectsAPI.GetIamPolicy(ctx, getReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving current iam policy: %w", err)
|
||||
}
|
||||
for _, binding := range input.Bindings {
|
||||
addIAMPolicy(policy, binding)
|
||||
}
|
||||
setReq := &iampb.SetIamPolicyRequest{
|
||||
Resource: "projects/" + c.project,
|
||||
Policy: policy,
|
||||
}
|
||||
if _, err := c.projectsAPI.SetIamPolicy(ctx, setReq); err != nil {
|
||||
return fmt.Errorf("setting new iam policy: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PolicyBinding is a GCP IAM policy binding.
|
||||
type PolicyBinding struct {
|
||||
ServiceAccount string
|
||||
Role string
|
||||
}
|
||||
|
||||
// addIAMPolicy inserts policy binding for service account and role to an existing iam policy.
|
||||
func addIAMPolicy(policy *iampb.Policy, policyBinding PolicyBinding) {
|
||||
var binding *iampb.Binding
|
||||
for _, existingBinding := range policy.Bindings {
|
||||
if existingBinding.Role == policyBinding.Role && existingBinding.Condition == nil {
|
||||
binding = existingBinding
|
||||
break
|
||||
}
|
||||
}
|
||||
if binding == nil {
|
||||
binding = &iampb.Binding{
|
||||
Role: policyBinding.Role,
|
||||
}
|
||||
policy.Bindings = append(policy.Bindings, binding)
|
||||
}
|
||||
|
||||
// add service account to role, if not already a member
|
||||
member := "serviceAccount:" + policyBinding.ServiceAccount
|
||||
var alreadyMember bool
|
||||
for _, existingMember := range binding.Members {
|
||||
if member == existingMember {
|
||||
alreadyMember = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !alreadyMember {
|
||||
binding.Members = append(binding.Members, member)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIAMPolicyBindingInput is the input for an AddIAMPolicyBinding operation.
|
||||
type AddIAMPolicyBindingInput struct {
|
||||
Bindings []PolicyBinding
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestAddIAMPolicyBindings(t *testing.T) {
|
||||
someErr := errors.New("someErr")
|
||||
|
||||
testCases := map[string]struct {
|
||||
projectsAPI stubProjectsAPI
|
||||
input AddIAMPolicyBindingInput
|
||||
wantErr bool
|
||||
}{
|
||||
"successful set without new bindings": {
|
||||
input: AddIAMPolicyBindingInput{
|
||||
Bindings: []PolicyBinding{},
|
||||
},
|
||||
},
|
||||
"successful set with bindings": {
|
||||
input: AddIAMPolicyBindingInput{
|
||||
Bindings: []PolicyBinding{
|
||||
{
|
||||
ServiceAccount: "service-account",
|
||||
Role: "role",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"retrieving iam policy fails": {
|
||||
projectsAPI: stubProjectsAPI{
|
||||
getPolicyErr: someErr,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"setting iam policy fails": {
|
||||
projectsAPI: stubProjectsAPI{
|
||||
setPolicyErr: someErr,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.Background()
|
||||
client := Client{
|
||||
project: "project",
|
||||
zone: "zone",
|
||||
name: "name",
|
||||
uid: "uid",
|
||||
projectsAPI: tc.projectsAPI,
|
||||
}
|
||||
|
||||
err := client.addIAMPolicyBindings(ctx, tc.input)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddIAMPolicy(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
binding PolicyBinding
|
||||
policy *iampb.Policy
|
||||
wantErr bool
|
||||
wantPolicy *iampb.Policy
|
||||
}{
|
||||
"successful on empty policy": {
|
||||
binding: PolicyBinding{
|
||||
ServiceAccount: "service-account",
|
||||
Role: "role",
|
||||
},
|
||||
policy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{},
|
||||
},
|
||||
wantPolicy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"serviceAccount:service-account"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"successful on existing policy with different role": {
|
||||
binding: PolicyBinding{
|
||||
ServiceAccount: "service-account",
|
||||
Role: "role",
|
||||
},
|
||||
policy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "other-role",
|
||||
Members: []string{"other-member"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPolicy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "other-role",
|
||||
Members: []string{"other-member"},
|
||||
},
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"serviceAccount:service-account"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"successful on existing policy with existing role": {
|
||||
binding: PolicyBinding{
|
||||
ServiceAccount: "service-account",
|
||||
Role: "role",
|
||||
},
|
||||
policy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"other-member"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPolicy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"other-member", "serviceAccount:service-account"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"already a member": {
|
||||
binding: PolicyBinding{
|
||||
ServiceAccount: "service-account",
|
||||
Role: "role",
|
||||
},
|
||||
policy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"serviceAccount:service-account"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPolicy: &iampb.Policy{
|
||||
Bindings: []*iampb.Binding{
|
||||
{
|
||||
Role: "role",
|
||||
Members: []string{"serviceAccount:service-account"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
addIAMPolicy(tc.policy, tc.binding)
|
||||
assert.True(proto.Equal(tc.wantPolicy, tc.policy))
|
||||
})
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package gcp
|
||||
|
||||
// KubeEnv contains placeholder values required by cluster-autoscaler.
|
||||
var KubeEnv = `AUTOSCALER_ENV_VARS: kube_reserved=cpu=1060m,memory=1019Mi,ephemeral-storage=41Gi;node_labels=;os=linux;os_distribution=cos;evictionHard=`
|
@ -17,21 +17,6 @@ type ConstellationState struct {
|
||||
CloudProvider string `json:"cloudprovider,omitempty"`
|
||||
LoadBalancerIP string `json:"bootstrapperhost,omitempty"`
|
||||
|
||||
GCPWorkerInstances cloudtypes.Instances `json:"gcpworkers,omitempty"`
|
||||
GCPControlPlaneInstances cloudtypes.Instances `json:"gcpcontrolplanes,omitempty"`
|
||||
GCPWorkerInstanceGroup string `json:"gcpworkerinstancegroup,omitempty"`
|
||||
GCPControlPlaneInstanceGroup string `json:"gcpcontrolplaneinstancegroup,omitempty"`
|
||||
GCPWorkerInstanceTemplate string `json:"gcpworkerinstancetemplate,omitempty"`
|
||||
GCPControlPlaneInstanceTemplate string `json:"gcpcontrolplaneinstancetemplate,omitempty"`
|
||||
GCPNetwork string `json:"gcpnetwork,omitempty"`
|
||||
GCPSubnetwork string `json:"gcpsubnetwork,omitempty"`
|
||||
GCPFirewalls []string `json:"gcpfirewalls,omitempty"`
|
||||
GCPLoadbalancerIPname string `json:"gcploadbalanceripid,omitempty"`
|
||||
GCPLoadbalancers []string `json:"gcploadbalancers,omitempty"`
|
||||
GCPProject string `json:"gcpproject,omitempty"`
|
||||
GCPZone string `json:"gcpzone,omitempty"`
|
||||
GCPRegion string `json:"gcpregion,omitempty"`
|
||||
|
||||
AzureWorkerInstances cloudtypes.Instances `json:"azureworkers,omitempty"`
|
||||
AzureControlPlaneInstances cloudtypes.Instances `json:"azurecontrolplanes,omitempty"`
|
||||
AzureResourceGroup string `json:"azureresourcegroup,omitempty"`
|
||||
|
Loading…
Reference in New Issue
Block a user