mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-06-20 04:04:21 -04:00
Manually manage GCP service accounts
This commit is contained in:
parent
f9c70d5c5a
commit
e761c9bf97
19 changed files with 186 additions and 555 deletions
|
@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Create multiple load balancers to enable load balacing TCP traffic for different backend services. All load balancers currently share the same public IP address.
|
- Create multiple load balancers to enable load balacing TCP traffic for different backend services. All load balancers currently share the same public IP address.
|
||||||
- Improve rollback on GCP resource termination. You can now terminate multiple times.
|
- Improve rollback on GCP resource termination. You can now terminate multiple times.
|
||||||
- Implement SSH peer to peer distribution between debugd nodes.
|
- Implement SSH peer to peer distribution between debugd nodes.
|
||||||
|
- GCP service account can now be managed manually.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
<!-- For changes in existing functionality. -->
|
<!-- For changes in existing functionality. -->
|
||||||
|
|
|
@ -15,12 +15,10 @@ type gcpclient interface {
|
||||||
CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error
|
CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error
|
||||||
CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error
|
CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error
|
||||||
CreateLoadBalancers(ctx context.Context) error
|
CreateLoadBalancers(ctx context.Context) error
|
||||||
CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error)
|
|
||||||
TerminateFirewall(ctx context.Context) error
|
TerminateFirewall(ctx context.Context) error
|
||||||
TerminateVPCs(context.Context) error
|
TerminateVPCs(context.Context) error
|
||||||
TerminateLoadBalancers(context.Context) error
|
TerminateLoadBalancers(context.Context) error
|
||||||
TerminateInstances(context.Context) error
|
TerminateInstances(context.Context) error
|
||||||
TerminateServiceAccount(ctx context.Context) error
|
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/edgelesssys/constellation/internal/azureshared"
|
"github.com/edgelesssys/constellation/internal/azureshared"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
|
||||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
|
||||||
"github.com/edgelesssys/constellation/internal/state"
|
"github.com/edgelesssys/constellation/internal/state"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
@ -244,7 +243,6 @@ type fakeGcpClient struct {
|
||||||
uid string
|
uid string
|
||||||
name string
|
name string
|
||||||
zone string
|
zone string
|
||||||
serviceAccount string
|
|
||||||
loadbalancers []string
|
loadbalancers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +262,6 @@ func (c *fakeGcpClient) GetState() state.ConstellationState {
|
||||||
Name: c.name,
|
Name: c.name,
|
||||||
UID: c.uid,
|
UID: c.uid,
|
||||||
GCPZone: c.zone,
|
GCPZone: c.zone,
|
||||||
GCPServiceAccount: c.serviceAccount,
|
|
||||||
GCPLoadbalancers: c.loadbalancers,
|
GCPLoadbalancers: c.loadbalancers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +280,6 @@ func (c *fakeGcpClient) SetState(stat state.ConstellationState) {
|
||||||
c.name = stat.Name
|
c.name = stat.Name
|
||||||
c.uid = stat.UID
|
c.uid = stat.UID
|
||||||
c.zone = stat.GCPZone
|
c.zone = stat.GCPZone
|
||||||
c.serviceAccount = stat.GCPServiceAccount
|
|
||||||
c.loadbalancers = stat.GCPLoadbalancers
|
c.loadbalancers = stat.GCPLoadbalancers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,22 +317,6 @@ func (c *fakeGcpClient) CreateInstances(ctx context.Context, input gcpcl.CreateI
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error) {
|
|
||||||
c.serviceAccount = "service-account@" + c.project + ".iam.gserviceaccount.com"
|
|
||||||
return gcpshared.ServiceAccountKey{
|
|
||||||
Type: "service_account",
|
|
||||||
ProjectID: c.project,
|
|
||||||
PrivateKeyID: "key-id",
|
|
||||||
PrivateKey: "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
|
|
||||||
ClientEmail: c.serviceAccount,
|
|
||||||
ClientID: "client-id",
|
|
||||||
AuthURI: "https://accounts.google.com/o/oauth2/auth",
|
|
||||||
TokenURI: "https://accounts.google.com/o/oauth2/token",
|
|
||||||
AuthProviderX509CertURL: "https://www.googleapis.com/oauth2/v1/certs",
|
|
||||||
ClientX509CertURL: "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email",
|
|
||||||
}.ToCloudServiceAccountURI(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) CreateLoadBalancers(ctx context.Context) error {
|
func (c *fakeGcpClient) CreateLoadBalancers(ctx context.Context) error {
|
||||||
c.loadbalancers = []string{"kube-lb", "boot-lb", "verify-lb"}
|
c.loadbalancers = []string{"kube-lb", "boot-lb", "verify-lb"}
|
||||||
return nil
|
return nil
|
||||||
|
@ -369,11 +349,6 @@ func (c *fakeGcpClient) TerminateInstances(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateServiceAccount(context.Context) error {
|
|
||||||
c.serviceAccount = ""
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateLoadBalancers(context.Context) error {
|
func (c *fakeGcpClient) TerminateLoadBalancers(context.Context) error {
|
||||||
c.loadbalancers = nil
|
c.loadbalancers = nil
|
||||||
return nil
|
return nil
|
||||||
|
@ -384,23 +359,20 @@ func (c *fakeGcpClient) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubGcpClient struct {
|
type stubGcpClient struct {
|
||||||
terminateFirewallCalled bool
|
terminateFirewallCalled bool
|
||||||
terminateInstancesCalled bool
|
terminateInstancesCalled bool
|
||||||
terminateVPCsCalled bool
|
terminateVPCsCalled bool
|
||||||
terminateServiceAccountCalled bool
|
closeCalled bool
|
||||||
closeCalled bool
|
|
||||||
|
|
||||||
createVPCsErr error
|
createVPCsErr error
|
||||||
createFirewallErr error
|
createFirewallErr error
|
||||||
createInstancesErr error
|
createInstancesErr error
|
||||||
createServiceAccountErr error
|
createLoadBalancerErr error
|
||||||
createLoadBalancerErr error
|
terminateFirewallErr error
|
||||||
terminateFirewallErr error
|
terminateVPCsErr error
|
||||||
terminateVPCsErr error
|
terminateInstancesErr error
|
||||||
terminateInstancesErr error
|
terminateLoadBalancerErr error
|
||||||
terminateServiceAccountErr error
|
closeErr error
|
||||||
terminateLoadBalancerErr error
|
|
||||||
closeErr error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubGcpClient) GetState() state.ConstellationState {
|
func (c *stubGcpClient) GetState() state.ConstellationState {
|
||||||
|
@ -422,10 +394,6 @@ func (c *stubGcpClient) CreateInstances(ctx context.Context, input gcpcl.CreateI
|
||||||
return c.createInstancesErr
|
return c.createInstancesErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateServiceAccount(ctx context.Context, input gcpcl.ServiceAccountInput) (string, error) {
|
|
||||||
return gcpshared.ServiceAccountKey{}.ToCloudServiceAccountURI(), c.createServiceAccountErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateLoadBalancers(ctx context.Context) error {
|
func (c *stubGcpClient) CreateLoadBalancers(ctx context.Context) error {
|
||||||
return c.createLoadBalancerErr
|
return c.createLoadBalancerErr
|
||||||
}
|
}
|
||||||
|
@ -445,11 +413,6 @@ func (c *stubGcpClient) TerminateInstances(context.Context) error {
|
||||||
return c.terminateInstancesErr
|
return c.terminateInstancesErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateServiceAccount(context.Context) error {
|
|
||||||
c.terminateServiceAccountCalled = true
|
|
||||||
return c.terminateServiceAccountErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateLoadBalancers(context.Context) error {
|
func (c *stubGcpClient) TerminateLoadBalancers(context.Context) error {
|
||||||
return c.terminateLoadBalancerErr
|
return c.terminateLoadBalancerErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,18 +34,7 @@ func (c *ServiceAccountCreator) Create(ctx context.Context, stat state.Constella
|
||||||
provider := cloudprovider.FromString(stat.CloudProvider)
|
provider := cloudprovider.FromString(stat.CloudProvider)
|
||||||
switch provider {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newGCPClient(ctx)
|
return "", state.ConstellationState{}, fmt.Errorf("creating service account not supported for GCP")
|
||||||
if err != nil {
|
|
||||||
return "", state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
defer cl.Close()
|
|
||||||
|
|
||||||
serviceAccount, stat, err := c.createServiceAccountGCP(ctx, cl, stat, config)
|
|
||||||
if err != nil {
|
|
||||||
return "", state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceAccount, stat, err
|
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newAzureClient(stat.AzureSubscription, stat.AzureTenant)
|
cl, err := c.newAzureClient(stat.AzureSubscription, stat.AzureTenant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,22 +54,6 @@ func (c *ServiceAccountCreator) Create(ctx context.Context, stat state.Constella
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ServiceAccountCreator) createServiceAccountGCP(ctx context.Context, cl gcpclient,
|
|
||||||
stat state.ConstellationState, config *config.Config,
|
|
||||||
) (string, state.ConstellationState, error) {
|
|
||||||
cl.SetState(stat)
|
|
||||||
|
|
||||||
input := gcpcl.ServiceAccountInput{
|
|
||||||
Roles: config.Provider.GCP.ServiceAccountRoles,
|
|
||||||
}
|
|
||||||
serviceAccount, err := cl.CreateServiceAccount(ctx, input)
|
|
||||||
if err != nil {
|
|
||||||
return "", state.ConstellationState{}, fmt.Errorf("creating service account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceAccount, cl.GetState(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ServiceAccountCreator) createServiceAccountAzure(ctx context.Context, cl azureclient,
|
func (c *ServiceAccountCreator) createServiceAccountAzure(ctx context.Context, cl azureclient,
|
||||||
stat state.ConstellationState, _ *config.Config,
|
stat state.ConstellationState, _ *config.Config,
|
||||||
) (string, state.ConstellationState, error) {
|
) (string, state.ConstellationState, error) {
|
||||||
|
|
|
@ -6,27 +6,12 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/internal/config"
|
"github.com/edgelesssys/constellation/internal/config"
|
||||||
"github.com/edgelesssys/constellation/internal/state"
|
"github.com/edgelesssys/constellation/internal/state"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServiceAccountCreator(t *testing.T) {
|
func TestServiceAccountCreator(t *testing.T) {
|
||||||
someGCPState := func() state.ConstellationState {
|
|
||||||
return state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.GCP.String(),
|
|
||||||
GCPProject: "project",
|
|
||||||
GCPWorkerInstances: cloudtypes.Instances{},
|
|
||||||
GCPControlPlaneInstances: cloudtypes.Instances{},
|
|
||||||
GCPWorkerInstanceGroup: "workers-group",
|
|
||||||
GCPControlPlaneInstanceGroup: "controlplane-group",
|
|
||||||
GCPWorkerInstanceTemplate: "template",
|
|
||||||
GCPControlPlaneInstanceTemplate: "template",
|
|
||||||
GCPNetwork: "network",
|
|
||||||
GCPFirewalls: []string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
someAzureState := func() state.ConstellationState {
|
someAzureState := func() state.ConstellationState {
|
||||||
return state.ConstellationState{
|
return state.ConstellationState{
|
||||||
CloudProvider: cloudprovider.Azure.String(),
|
CloudProvider: cloudprovider.Azure.String(),
|
||||||
|
@ -42,32 +27,6 @@ func TestServiceAccountCreator(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantStateMutator func(*state.ConstellationState)
|
wantStateMutator func(*state.ConstellationState)
|
||||||
}{
|
}{
|
||||||
"gcp": {
|
|
||||||
newGCPClient: func(ctx context.Context) (gcpclient, error) {
|
|
||||||
return &fakeGcpClient{}, nil
|
|
||||||
},
|
|
||||||
state: someGCPState(),
|
|
||||||
config: config.Default(),
|
|
||||||
wantStateMutator: func(stat *state.ConstellationState) {
|
|
||||||
stat.GCPServiceAccount = "service-account@project.iam.gserviceaccount.com"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"gcp newGCPClient error": {
|
|
||||||
newGCPClient: func(ctx context.Context) (gcpclient, error) {
|
|
||||||
return nil, someErr
|
|
||||||
},
|
|
||||||
state: someGCPState(),
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"gcp client createServiceAccount error": {
|
|
||||||
newGCPClient: func(ctx context.Context) (gcpclient, error) {
|
|
||||||
return &stubGcpClient{createServiceAccountErr: someErr}, nil
|
|
||||||
},
|
|
||||||
state: someGCPState(),
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"azure": {
|
"azure": {
|
||||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
||||||
return &fakeAzureClient{}, nil
|
return &fakeAzureClient{}, nil
|
||||||
|
|
|
@ -65,7 +65,8 @@ func (t *Terminator) terminateGCP(ctx context.Context, cl gcpclient, state state
|
||||||
if err := cl.TerminateVPCs(ctx); err != nil {
|
if err := cl.TerminateVPCs(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return cl.TerminateServiceAccount(ctx)
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateAzure(ctx context.Context, cl azureclient, state state.ConstellationState) error {
|
func (t *Terminator) terminateAzure(ctx context.Context, cl azureclient, state state.ConstellationState) error {
|
||||||
|
|
|
@ -29,7 +29,6 @@ func TestTerminator(t *testing.T) {
|
||||||
GCPControlPlaneInstanceTemplate: "template",
|
GCPControlPlaneInstanceTemplate: "template",
|
||||||
GCPNetwork: "network",
|
GCPNetwork: "network",
|
||||||
GCPFirewalls: []string{"a", "b", "c"},
|
GCPFirewalls: []string{"a", "b", "c"},
|
||||||
GCPServiceAccount: "service-account@project.iam.gserviceaccount.com",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
someAzureState := func() state.ConstellationState {
|
someAzureState := func() state.ConstellationState {
|
||||||
|
@ -80,11 +79,6 @@ func TestTerminator(t *testing.T) {
|
||||||
state: someGCPState(),
|
state: someGCPState(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp terminateServiceAccount error": {
|
|
||||||
gcpclient: &stubGcpClient{terminateServiceAccountErr: someErr},
|
|
||||||
state: someGCPState(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"azure": {
|
"azure": {
|
||||||
azureclient: &stubAzureClient{},
|
azureclient: &stubAzureClient{},
|
||||||
state: someAzureState(),
|
state: someAzureState(),
|
||||||
|
@ -135,7 +129,6 @@ func TestTerminator(t *testing.T) {
|
||||||
assert.True(cl.terminateFirewallCalled)
|
assert.True(cl.terminateFirewallCalled)
|
||||||
assert.True(cl.terminateInstancesCalled)
|
assert.True(cl.terminateInstancesCalled)
|
||||||
assert.True(cl.terminateVPCsCalled)
|
assert.True(cl.terminateVPCsCalled)
|
||||||
assert.True(cl.terminateServiceAccountCalled)
|
|
||||||
assert.True(cl.closeCalled)
|
assert.True(cl.closeCalled)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl := tc.azureclient.(*stubAzureClient)
|
cl := tc.azureclient.(*stubAzureClient)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/edgelesssys/constellation/internal/crypto"
|
"github.com/edgelesssys/constellation/internal/crypto"
|
||||||
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
"github.com/edgelesssys/constellation/internal/deploy/ssh"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
|
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
||||||
grpcRetry "github.com/edgelesssys/constellation/internal/grpc/retry"
|
grpcRetry "github.com/edgelesssys/constellation/internal/grpc/retry"
|
||||||
"github.com/edgelesssys/constellation/internal/license"
|
"github.com/edgelesssys/constellation/internal/license"
|
||||||
|
@ -116,13 +117,22 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Println("Creating service account ...")
|
var serviceAccURI string
|
||||||
serviceAccount, stat, err := serviceAccCreator.Create(cmd.Context(), stat, config)
|
// Temporary legacy flow for Azure.
|
||||||
if err != nil {
|
if provider == cloudprovider.Azure {
|
||||||
return err
|
cmd.Println("Creating service account ...")
|
||||||
}
|
serviceAccURI, stat, err = serviceAccCreator.Create(cmd.Context(), stat, config)
|
||||||
if err := fileHandler.WriteJSON(constants.StateFilename, stat, file.OptOverwrite); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
if err := fileHandler.WriteJSON(constants.StateFilename, stat, file.OptOverwrite); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serviceAccURI, err = getMarschaledServiceAccountURI(provider, config, fileHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
workers, err := getScalingGroupsFromState(stat, config)
|
workers, err := getScalingGroupsFromState(stat, config)
|
||||||
|
@ -150,7 +160,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
|
||||||
StorageUri: kms.NoStoreURI,
|
StorageUri: kms.NoStoreURI,
|
||||||
KeyEncryptionKeyId: "",
|
KeyEncryptionKeyId: "",
|
||||||
UseExistingKek: false,
|
UseExistingKek: false,
|
||||||
CloudServiceAccountUri: serviceAccount,
|
CloudServiceAccountUri: serviceAccURI,
|
||||||
KubernetesVersion: config.KubernetesVersion,
|
KubernetesVersion: config.KubernetesVersion,
|
||||||
SshUserKeys: ssh.ToProtoSlice(sshUsers),
|
SshUserKeys: ssh.ToProtoSlice(sshUsers),
|
||||||
HelmDeployments: helmDeployments,
|
HelmDeployments: helmDeployments,
|
||||||
|
@ -352,6 +362,29 @@ func readIPFromIDFile(fileHandler file.Handler) (string, error) {
|
||||||
return idFile.IP, nil
|
return idFile.IP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMarschaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) {
|
||||||
|
switch provider {
|
||||||
|
case cloudprovider.GCP:
|
||||||
|
path := config.Provider.GCP.ServiceAccountKeyPath
|
||||||
|
|
||||||
|
var key gcpshared.ServiceAccountKey
|
||||||
|
if err := fileHandler.ReadJSON(path, &key); err != nil {
|
||||||
|
return "", fmt.Errorf("reading service account key from path %q: %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.ToCloudServiceAccountURI(), nil
|
||||||
|
|
||||||
|
case cloudprovider.Azure:
|
||||||
|
return "", fmt.Errorf("TODO")
|
||||||
|
|
||||||
|
case cloudprovider.QEMU:
|
||||||
|
return "", nil // QEMU does not use service account keys
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported cloud provider %q", provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getScalingGroupsFromState(stat state.ConstellationState, config *config.Config) (workers cloudtypes.ScalingGroup, err error) {
|
func getScalingGroupsFromState(stat state.ConstellationState, config *config.Config) (workers cloudtypes.ScalingGroup, err error) {
|
||||||
switch cloudprovider.FromString(stat.CloudProvider) {
|
switch cloudprovider.FromString(stat.CloudProvider) {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/edgelesssys/constellation/internal/config"
|
"github.com/edgelesssys/constellation/internal/config"
|
||||||
"github.com/edgelesssys/constellation/internal/constants"
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
|
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
|
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
|
||||||
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
||||||
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
|
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
|
||||||
|
@ -43,16 +44,19 @@ func TestInitArgumentValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitialize(t *testing.T) {
|
func TestInitialize(t *testing.T) {
|
||||||
testGcpState := state.ConstellationState{
|
testGcpState := &state.ConstellationState{
|
||||||
CloudProvider: "GCP",
|
CloudProvider: "GCP",
|
||||||
GCPWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
GCPWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
||||||
}
|
}
|
||||||
testAzureState := state.ConstellationState{
|
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
||||||
|
Type: "service_account",
|
||||||
|
}
|
||||||
|
testAzureState := &state.ConstellationState{
|
||||||
CloudProvider: "Azure",
|
CloudProvider: "Azure",
|
||||||
AzureWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
AzureWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
||||||
AzureResourceGroup: "test",
|
AzureResourceGroup: "test",
|
||||||
}
|
}
|
||||||
testQemuState := state.ConstellationState{
|
testQemuState := &state.ConstellationState{
|
||||||
CloudProvider: "QEMU",
|
CloudProvider: "QEMU",
|
||||||
QEMUWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
QEMUWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
||||||
}
|
}
|
||||||
|
@ -61,77 +65,86 @@ func TestInitialize(t *testing.T) {
|
||||||
OwnerId: []byte("ownerID"),
|
OwnerId: []byte("ownerID"),
|
||||||
ClusterId: []byte("clusterID"),
|
ClusterId: []byte("clusterID"),
|
||||||
}
|
}
|
||||||
|
serviceAccPath := "/test/service-account.json"
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
existingState state.ConstellationState
|
state *state.ConstellationState
|
||||||
existingIDFile clusterIDsFile
|
existingIDFile *clusterIDsFile
|
||||||
serviceAccountCreator stubServiceAccountCreator
|
serviceAccCreator serviceAccountCreator
|
||||||
helmLoader stubHelmLoader
|
configMutator func(*config.Config)
|
||||||
initServerAPI *stubInitServer
|
serviceAccKey *gcpshared.ServiceAccountKey
|
||||||
endpointFlag string
|
helmLoader stubHelmLoader
|
||||||
setAutoscaleFlag bool
|
initServerAPI *stubInitServer
|
||||||
wantErr bool
|
endpointFlag string
|
||||||
|
setAutoscaleFlag bool
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"initialize some gcp instances": {
|
"initialize some gcp instances": {
|
||||||
existingState: testGcpState,
|
state: testGcpState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
|
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
serviceAccKey: gcpServiceAccKey,
|
||||||
},
|
|
||||||
"initialize some azure instances": {
|
|
||||||
existingState: testAzureState,
|
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
},
|
},
|
||||||
|
"initialize some azure instances": {
|
||||||
|
state: testAzureState,
|
||||||
|
serviceAccCreator: &stubServiceAccountCreator{},
|
||||||
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
|
},
|
||||||
"initialize some qemu instances": {
|
"initialize some qemu instances": {
|
||||||
existingState: testQemuState,
|
state: testQemuState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
},
|
},
|
||||||
"initialize gcp with autoscaling": {
|
"initialize gcp with autoscaling": {
|
||||||
existingState: testGcpState,
|
state: testGcpState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
|
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||||
|
serviceAccKey: gcpServiceAccKey,
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
setAutoscaleFlag: true,
|
setAutoscaleFlag: true,
|
||||||
},
|
},
|
||||||
"initialize azure with autoscaling": {
|
"initialize azure with autoscaling": {
|
||||||
existingState: testAzureState,
|
state: testAzureState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
serviceAccCreator: &stubServiceAccountCreator{},
|
||||||
setAutoscaleFlag: true,
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
|
setAutoscaleFlag: true,
|
||||||
},
|
},
|
||||||
"initialize with endpoint flag": {
|
"initialize with endpoint flag": {
|
||||||
existingState: testGcpState,
|
state: testGcpState,
|
||||||
|
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||||
|
serviceAccKey: gcpServiceAccKey,
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
endpointFlag: "192.0.2.1",
|
endpointFlag: "192.0.2.1",
|
||||||
},
|
},
|
||||||
"empty state": {
|
"empty state": {
|
||||||
existingState: state.ConstellationState{},
|
state: &state.ConstellationState{},
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{},
|
initServerAPI: &stubInitServer{},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"neither endpoint flag nor id file": {
|
"neither endpoint flag nor id file": {
|
||||||
existingState: state.ConstellationState{},
|
state: &state.ConstellationState{},
|
||||||
initServerAPI: &stubInitServer{},
|
wantErr: true,
|
||||||
wantErr: true,
|
|
||||||
},
|
},
|
||||||
"init call fails": {
|
"init call fails": {
|
||||||
existingState: testGcpState,
|
state: testGcpState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initErr: someErr},
|
initServerAPI: &stubInitServer{initErr: someErr},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"fail to create service account": {
|
"fail to create service account": {
|
||||||
existingState: testGcpState,
|
state: testAzureState,
|
||||||
existingIDFile: clusterIDsFile{IP: "192.0.2.1"},
|
existingIDFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{},
|
initServerAPI: &stubInitServer{},
|
||||||
serviceAccountCreator: stubServiceAccountCreator{createErr: someErr},
|
serviceAccCreator: &stubServiceAccountCreator{createErr: someErr},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"fail to load helm charts": {
|
"fail to load helm charts": {
|
||||||
existingState: testGcpState,
|
state: testGcpState,
|
||||||
helmLoader: stubHelmLoader{loadErr: someErr},
|
helmLoader: stubHelmLoader{loadErr: someErr},
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -143,6 +156,7 @@ func TestInitialize(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
|
// Networking
|
||||||
netDialer := testdialer.NewBufconnDialer()
|
netDialer := testdialer.NewBufconnDialer()
|
||||||
newDialer := func(*cloudcmd.Validator) *dialer.Dialer {
|
newDialer := func(*cloudcmd.Validator) *dialer.Dialer {
|
||||||
return dialer.New(nil, nil, netDialer)
|
return dialer.New(nil, nil, netDialer)
|
||||||
|
@ -155,30 +169,44 @@ func TestInitialize(t *testing.T) {
|
||||||
go initServer.Serve(listener)
|
go initServer.Serve(listener)
|
||||||
defer initServer.GracefulStop()
|
defer initServer.GracefulStop()
|
||||||
|
|
||||||
|
// Command
|
||||||
cmd := NewInitCmd()
|
cmd := NewInitCmd()
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.SetOut(&out)
|
cmd.SetOut(&out)
|
||||||
var errOut bytes.Buffer
|
var errOut bytes.Buffer
|
||||||
cmd.SetErr(&errOut)
|
cmd.SetErr(&errOut)
|
||||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
fileHandler := file.NewHandler(fs)
|
|
||||||
|
|
||||||
config := defaultConfigWithExpectedMeasurements(t, cloudprovider.FromString(tc.existingState.CloudProvider))
|
// Flags
|
||||||
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))
|
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, tc.existingState, file.OptNone))
|
|
||||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.existingIDFile, file.OptNone))
|
|
||||||
require.NoError(cmd.Flags().Set("autoscale", strconv.FormatBool(tc.setAutoscaleFlag)))
|
require.NoError(cmd.Flags().Set("autoscale", strconv.FormatBool(tc.setAutoscaleFlag)))
|
||||||
if tc.endpointFlag != "" {
|
if tc.endpointFlag != "" {
|
||||||
require.NoError(cmd.Flags().Set("endpoint", tc.endpointFlag))
|
require.NoError(cmd.Flags().Set("endpoint", tc.endpointFlag))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File system preparation
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
fileHandler := file.NewHandler(fs)
|
||||||
|
config := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.FromString(tc.state.CloudProvider))
|
||||||
|
if tc.configMutator != nil {
|
||||||
|
tc.configMutator(config)
|
||||||
|
}
|
||||||
|
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config, file.OptNone))
|
||||||
|
if tc.state != nil {
|
||||||
|
require.NoError(fileHandler.WriteJSON(constants.StateFilename, tc.state, file.OptNone))
|
||||||
|
}
|
||||||
|
if tc.existingIDFile != nil {
|
||||||
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.existingIDFile, file.OptNone))
|
||||||
|
}
|
||||||
|
if tc.serviceAccKey != nil {
|
||||||
|
require.NoError(fileHandler.WriteJSON(serviceAccPath, tc.serviceAccKey, file.OptNone))
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
cmd.SetContext(ctx)
|
cmd.SetContext(ctx)
|
||||||
|
|
||||||
err := initialize(cmd, newDialer, &tc.serviceAccountCreator, fileHandler, &tc.helmLoader, &stubLicenseClient{})
|
err := initialize(cmd, newDialer, tc.serviceAccCreator, fileHandler, &tc.helmLoader, &stubLicenseClient{})
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
@ -510,28 +538,30 @@ func (s *stubInitServer) Init(ctx context.Context, req *initproto.InitRequest) (
|
||||||
return s.initResp, s.initErr
|
return s.initResp, s.initErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultConfigWithExpectedMeasurements(t *testing.T, csp cloudprovider.Provider) *config.Config {
|
func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, csp cloudprovider.Provider) *config.Config {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
config := config.Default()
|
|
||||||
|
|
||||||
config.Provider.Azure.SubscriptionID = "01234567-0123-0123-0123-0123456789ab"
|
switch csp {
|
||||||
config.Provider.Azure.TenantID = "01234567-0123-0123-0123-0123456789ab"
|
case cloudprovider.Azure:
|
||||||
config.Provider.Azure.Location = "test-location"
|
conf.Provider.Azure.SubscriptionID = "01234567-0123-0123-0123-0123456789ab"
|
||||||
config.Provider.Azure.UserAssignedIdentity = "test-identity"
|
conf.Provider.Azure.TenantID = "01234567-0123-0123-0123-0123456789ab"
|
||||||
config.Provider.Azure.Measurements[8] = []byte("00000000000000000000000000000000")
|
conf.Provider.Azure.Location = "test-location"
|
||||||
config.Provider.Azure.Measurements[9] = []byte("11111111111111111111111111111111")
|
conf.Provider.Azure.UserAssignedIdentity = "test-identity"
|
||||||
|
conf.Provider.Azure.Measurements[8] = []byte("00000000000000000000000000000000")
|
||||||
|
conf.Provider.Azure.Measurements[9] = []byte("11111111111111111111111111111111")
|
||||||
|
case cloudprovider.GCP:
|
||||||
|
conf.Provider.GCP.Region = "test-region"
|
||||||
|
conf.Provider.GCP.Project = "test-project"
|
||||||
|
conf.Provider.GCP.Zone = "test-zone"
|
||||||
|
conf.Provider.GCP.Measurements[8] = []byte("00000000000000000000000000000000")
|
||||||
|
conf.Provider.GCP.Measurements[9] = []byte("11111111111111111111111111111111")
|
||||||
|
case cloudprovider.QEMU:
|
||||||
|
conf.Provider.QEMU.Measurements[8] = []byte("00000000000000000000000000000000")
|
||||||
|
conf.Provider.QEMU.Measurements[9] = []byte("11111111111111111111111111111111")
|
||||||
|
}
|
||||||
|
|
||||||
config.Provider.GCP.Region = "test-region"
|
conf.RemoveProviderExcept(csp)
|
||||||
config.Provider.GCP.Project = "test-project"
|
return conf
|
||||||
config.Provider.GCP.Zone = "test-zone"
|
|
||||||
config.Provider.GCP.Measurements[8] = []byte("00000000000000000000000000000000")
|
|
||||||
config.Provider.GCP.Measurements[9] = []byte("11111111111111111111111111111111")
|
|
||||||
|
|
||||||
config.Provider.QEMU.Measurements[8] = []byte("00000000000000000000000000000000")
|
|
||||||
config.Provider.QEMU.Measurements[9] = []byte("11111111111111111111111111111111")
|
|
||||||
|
|
||||||
config.RemoveProviderExcept(csp)
|
|
||||||
return config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubLicenseClient struct{}
|
type stubLicenseClient struct{}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/internal/config"
|
||||||
"github.com/edgelesssys/constellation/internal/constants"
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/internal/crypto/testvector"
|
"github.com/edgelesssys/constellation/internal/crypto/testvector"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
|
@ -146,7 +147,7 @@ func TestRecover(t *testing.T) {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
|
|
||||||
config := defaultConfigWithExpectedMeasurements(t, cloudprovider.FromString(tc.existingState.CloudProvider))
|
config := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.FromString(tc.existingState.CloudProvider))
|
||||||
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))
|
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))
|
||||||
|
|
||||||
require.NoError(fileHandler.WriteJSON("constellation-mastersecret.json", masterSecret{Key: tc.masterSecret.Secret, Salt: tc.masterSecret.Salt}, file.OptNone))
|
require.NoError(fileHandler.WriteJSON("constellation-mastersecret.json", masterSecret{Key: tc.masterSecret.Secret, Salt: tc.masterSecret.Salt}, file.OptNone))
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/internal/atls"
|
"github.com/edgelesssys/constellation/internal/atls"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/internal/config"
|
||||||
"github.com/edgelesssys/constellation/internal/constants"
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
"github.com/edgelesssys/constellation/internal/grpc/dialer"
|
||||||
|
@ -190,7 +191,7 @@ func TestVerify(t *testing.T) {
|
||||||
}
|
}
|
||||||
fileHandler := file.NewHandler(tc.setupFs(require))
|
fileHandler := file.NewHandler(tc.setupFs(require))
|
||||||
|
|
||||||
config := defaultConfigWithExpectedMeasurements(t, tc.provider)
|
config := defaultConfigWithExpectedMeasurements(t, config.Default(), tc.provider)
|
||||||
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))
|
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))
|
||||||
if tc.idFile != nil {
|
if tc.idFile != nil {
|
||||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
|
||||||
|
|
|
@ -2,15 +2,12 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/googleapis/gax-go/v2"
|
"github.com/googleapis/gax-go/v2"
|
||||||
"google.golang.org/api/iterator"
|
"google.golang.org/api/iterator"
|
||||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
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"
|
iampb "google.golang.org/genproto/googleapis/iam/v1"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type stubOperation struct {
|
type stubOperation struct {
|
||||||
|
@ -436,54 +433,6 @@ func (a stubInstanceGroupManagersAPI) ListManagedInstances(ctx context.Context,
|
||||||
return a.listIterator
|
return a.listIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubIAMAPI struct {
|
|
||||||
serviceAccountKeyData []byte
|
|
||||||
createErr error
|
|
||||||
createKeyErr error
|
|
||||||
deleteServiceAccountErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a stubIAMAPI) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a stubIAMAPI) CreateServiceAccount(ctx context.Context, req *adminpb.CreateServiceAccountRequest, opts ...gax.CallOption) (*adminpb.ServiceAccount, error) {
|
|
||||||
if a.createErr != nil {
|
|
||||||
return nil, a.createErr
|
|
||||||
}
|
|
||||||
return &adminpb.ServiceAccount{
|
|
||||||
Name: "name",
|
|
||||||
ProjectId: "project-id",
|
|
||||||
UniqueId: "unique-id",
|
|
||||||
Email: "email",
|
|
||||||
DisplayName: "display-name",
|
|
||||||
Description: "description",
|
|
||||||
Oauth2ClientId: "oauth2-client-id",
|
|
||||||
Disabled: false,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a stubIAMAPI) CreateServiceAccountKey(ctx context.Context, req *adminpb.CreateServiceAccountKeyRequest, opts ...gax.CallOption) (*adminpb.ServiceAccountKey, error) {
|
|
||||||
if a.createKeyErr != nil {
|
|
||||||
return nil, a.createKeyErr
|
|
||||||
}
|
|
||||||
return &adminpb.ServiceAccountKey{
|
|
||||||
Name: "name",
|
|
||||||
PrivateKeyType: adminpb.ServiceAccountPrivateKeyType_TYPE_GOOGLE_CREDENTIALS_FILE,
|
|
||||||
KeyAlgorithm: adminpb.ServiceAccountKeyAlgorithm_KEY_ALG_RSA_2048,
|
|
||||||
PrivateKeyData: a.serviceAccountKeyData,
|
|
||||||
PublicKeyData: []byte("public-key-data"),
|
|
||||||
ValidAfterTime: timestamppb.New(time.Time{}),
|
|
||||||
ValidBeforeTime: timestamppb.New(time.Time{}),
|
|
||||||
KeyOrigin: adminpb.ServiceAccountKeyOrigin_GOOGLE_PROVIDED,
|
|
||||||
KeyType: adminpb.ListServiceAccountKeysRequest_USER_MANAGED,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a stubIAMAPI) DeleteServiceAccount(ctx context.Context, req *adminpb.DeleteServiceAccountRequest, opts ...gax.CallOption) error {
|
|
||||||
return a.deleteServiceAccountErr
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubProjectsAPI struct {
|
type stubProjectsAPI struct {
|
||||||
getPolicyErr error
|
getPolicyErr error
|
||||||
setPolicyErr error
|
setPolicyErr error
|
||||||
|
|
|
@ -55,7 +55,6 @@ type Client struct {
|
||||||
uid string
|
uid string
|
||||||
zone string
|
zone string
|
||||||
region string
|
region string
|
||||||
serviceAccount string
|
|
||||||
|
|
||||||
// loadbalancer
|
// loadbalancer
|
||||||
loadbalancerIP string
|
loadbalancerIP string
|
||||||
|
@ -270,7 +269,6 @@ func (c *Client) GetState() state.ConstellationState {
|
||||||
GCPFirewalls: c.firewalls,
|
GCPFirewalls: c.firewalls,
|
||||||
GCPNetwork: c.network,
|
GCPNetwork: c.network,
|
||||||
GCPSubnetwork: c.subnetwork,
|
GCPSubnetwork: c.subnetwork,
|
||||||
GCPServiceAccount: c.serviceAccount,
|
|
||||||
GCPLoadbalancerIPname: c.loadbalancerIPname,
|
GCPLoadbalancerIPname: c.loadbalancerIPname,
|
||||||
}
|
}
|
||||||
for _, lb := range c.loadbalancers {
|
for _, lb := range c.loadbalancers {
|
||||||
|
@ -297,7 +295,6 @@ func (c *Client) SetState(stat state.ConstellationState) {
|
||||||
c.controlPlaneTemplate = stat.GCPControlPlaneInstanceTemplate
|
c.controlPlaneTemplate = stat.GCPControlPlaneInstanceTemplate
|
||||||
c.loadbalancerIPname = stat.GCPLoadbalancerIPname
|
c.loadbalancerIPname = stat.GCPLoadbalancerIPname
|
||||||
c.loadbalancerIP = stat.LoadBalancerIP
|
c.loadbalancerIP = stat.LoadBalancerIP
|
||||||
c.serviceAccount = stat.GCPServiceAccount
|
|
||||||
for _, lbName := range stat.GCPLoadbalancers {
|
for _, lbName := range stat.GCPLoadbalancers {
|
||||||
lb := &loadBalancer{
|
lb := &loadBalancer{
|
||||||
name: lbName,
|
name: lbName,
|
||||||
|
|
|
@ -69,7 +69,6 @@ func TestSetGetState(t *testing.T) {
|
||||||
assert.Equal(state.GCPFirewalls, client.firewalls)
|
assert.Equal(state.GCPFirewalls, client.firewalls)
|
||||||
assert.Equal(state.GCPControlPlaneInstanceTemplate, client.controlPlaneTemplate)
|
assert.Equal(state.GCPControlPlaneInstanceTemplate, client.controlPlaneTemplate)
|
||||||
assert.Equal(state.GCPWorkerInstanceTemplate, client.workerTemplate)
|
assert.Equal(state.GCPWorkerInstanceTemplate, client.workerTemplate)
|
||||||
assert.Equal(state.GCPServiceAccount, client.serviceAccount)
|
|
||||||
assert.Equal(state.LoadBalancerIP, client.loadbalancerIP)
|
assert.Equal(state.LoadBalancerIP, client.loadbalancerIP)
|
||||||
for _, lb := range client.loadbalancers {
|
for _, lb := range client.loadbalancers {
|
||||||
assert.Contains(state.GCPLoadbalancers, lb.name)
|
assert.Contains(state.GCPLoadbalancers, lb.name)
|
||||||
|
@ -97,7 +96,6 @@ func TestSetGetState(t *testing.T) {
|
||||||
firewalls: state.GCPFirewalls,
|
firewalls: state.GCPFirewalls,
|
||||||
workerTemplate: state.GCPWorkerInstanceTemplate,
|
workerTemplate: state.GCPWorkerInstanceTemplate,
|
||||||
controlPlaneTemplate: state.GCPControlPlaneInstanceTemplate,
|
controlPlaneTemplate: state.GCPControlPlaneInstanceTemplate,
|
||||||
serviceAccount: state.GCPServiceAccount,
|
|
||||||
loadbalancerIP: state.LoadBalancerIP,
|
loadbalancerIP: state.LoadBalancerIP,
|
||||||
loadbalancerIPname: state.GCPLoadbalancerIPname,
|
loadbalancerIPname: state.GCPLoadbalancerIPname,
|
||||||
}
|
}
|
||||||
|
@ -111,18 +109,6 @@ func TestSetGetState(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 TestBuildResourceName(t *testing.T) {
|
func TestBuildResourceName(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
clientUID string
|
clientUID string
|
||||||
|
@ -209,6 +195,18 @@ func TestResourceURI(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestCloseAll(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
|
||||||
adminpb "google.golang.org/genproto/googleapis/iam/admin/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateServiceAccount creates a new GCP service account and returns an account key as service account URI.
|
|
||||||
func (c *Client) CreateServiceAccount(ctx context.Context, input ServiceAccountInput) (string, error) {
|
|
||||||
insertInput := insertServiceAccountInput{
|
|
||||||
Project: c.project,
|
|
||||||
AccountID: "constellation-app-" + c.uid,
|
|
||||||
DisplayName: "constellation-app-" + c.uid,
|
|
||||||
Description: "This service account belongs to a Constellation cluster.",
|
|
||||||
}
|
|
||||||
|
|
||||||
email, err := c.insertServiceAccount(ctx, insertInput)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
c.serviceAccount = email
|
|
||||||
|
|
||||||
iamInput := input.addIAMPolicyBindingInput(c.serviceAccount)
|
|
||||||
if err := c.addIAMPolicyBindings(ctx, iamInput); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := c.createServiceAccountKey(ctx, email)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.ToCloudServiceAccountURI(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) TerminateServiceAccount(ctx context.Context) error {
|
|
||||||
if c.serviceAccount == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &adminpb.DeleteServiceAccountRequest{
|
|
||||||
Name: "projects/-/serviceAccounts/" + c.serviceAccount,
|
|
||||||
}
|
|
||||||
if err := c.iamAPI.DeleteServiceAccount(ctx, req); err != nil && !isNotFoundError(err) {
|
|
||||||
return fmt.Errorf("deleting service account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.serviceAccount = ""
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceAccountInput struct {
|
|
||||||
Roles []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ServiceAccountInput) addIAMPolicyBindingInput(serviceAccount string) AddIAMPolicyBindingInput {
|
|
||||||
iamPolicyBindingInput := AddIAMPolicyBindingInput{
|
|
||||||
Bindings: make([]PolicyBinding, len(i.Roles)),
|
|
||||||
}
|
|
||||||
for i, role := range i.Roles {
|
|
||||||
iamPolicyBindingInput.Bindings[i] = PolicyBinding{
|
|
||||||
ServiceAccount: serviceAccount,
|
|
||||||
Role: role,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iamPolicyBindingInput
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) insertServiceAccount(ctx context.Context, input insertServiceAccountInput) (string, error) {
|
|
||||||
req := input.createServiceAccountRequest()
|
|
||||||
account, err := c.iamAPI.CreateServiceAccount(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return account.Email, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) createServiceAccountKey(ctx context.Context, email string) (gcpshared.ServiceAccountKey, error) {
|
|
||||||
req := createServiceAccountKeyRequest(email)
|
|
||||||
key, err := c.iamAPI.CreateServiceAccountKey(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return gcpshared.ServiceAccountKey{}, fmt.Errorf("creating service account key: %w", err)
|
|
||||||
}
|
|
||||||
var serviceAccountKey gcpshared.ServiceAccountKey
|
|
||||||
if err := json.Unmarshal(key.PrivateKeyData, &serviceAccountKey); err != nil {
|
|
||||||
return gcpshared.ServiceAccountKey{}, fmt.Errorf("decoding service account key JSON: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceAccountKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// insertServiceAccountInput is the input for a createServiceAccount operation.
|
|
||||||
type insertServiceAccountInput struct {
|
|
||||||
Project string
|
|
||||||
AccountID string
|
|
||||||
DisplayName string
|
|
||||||
Description string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c insertServiceAccountInput) createServiceAccountRequest() *adminpb.CreateServiceAccountRequest {
|
|
||||||
return &adminpb.CreateServiceAccountRequest{
|
|
||||||
Name: "projects/" + c.Project,
|
|
||||||
AccountId: c.AccountID,
|
|
||||||
ServiceAccount: &adminpb.ServiceAccount{
|
|
||||||
DisplayName: c.DisplayName,
|
|
||||||
Description: c.Description,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createServiceAccountKeyRequest(email string) *adminpb.CreateServiceAccountKeyRequest {
|
|
||||||
return &adminpb.CreateServiceAccountKeyRequest{
|
|
||||||
Name: "projects/-/serviceAccounts/" + email,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCreateServiceAccount(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
someErr := errors.New("someErr")
|
|
||||||
key := gcpshared.ServiceAccountKey{
|
|
||||||
Type: "type",
|
|
||||||
ProjectID: "project-id",
|
|
||||||
PrivateKeyID: "private-key-id",
|
|
||||||
PrivateKey: "private-key",
|
|
||||||
ClientEmail: "client-email",
|
|
||||||
ClientID: "client-id",
|
|
||||||
AuthURI: "auth-uri",
|
|
||||||
TokenURI: "token-uri",
|
|
||||||
AuthProviderX509CertURL: "auth-provider-x509-cert-url",
|
|
||||||
ClientX509CertURL: "client-x509-cert-url",
|
|
||||||
}
|
|
||||||
keyData, err := json.Marshal(key)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
iamAPI iamAPI
|
|
||||||
projectsAPI stubProjectsAPI
|
|
||||||
input ServiceAccountInput
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"successful create": {
|
|
||||||
iamAPI: stubIAMAPI{serviceAccountKeyData: keyData},
|
|
||||||
input: ServiceAccountInput{
|
|
||||||
Roles: []string{"someRole"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"successful create with roles": {
|
|
||||||
iamAPI: stubIAMAPI{serviceAccountKeyData: keyData},
|
|
||||||
},
|
|
||||||
"creating account fails": {
|
|
||||||
iamAPI: stubIAMAPI{createErr: someErr},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"creating account key fails": {
|
|
||||||
iamAPI: stubIAMAPI{createKeyErr: someErr},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"key data missing": {
|
|
||||||
iamAPI: stubIAMAPI{},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"key data corrupt": {
|
|
||||||
iamAPI: stubIAMAPI{serviceAccountKeyData: []byte("invalid key data")},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"retrieving iam policy bindings fails": {
|
|
||||||
iamAPI: stubIAMAPI{},
|
|
||||||
projectsAPI: stubProjectsAPI{getPolicyErr: someErr},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"setting iam policy bindings fails": {
|
|
||||||
iamAPI: stubIAMAPI{},
|
|
||||||
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",
|
|
||||||
iamAPI: tc.iamAPI,
|
|
||||||
projectsAPI: tc.projectsAPI,
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceAccountKey, err := client.CreateServiceAccount(ctx, tc.input)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.Equal(key.ToCloudServiceAccountURI(), serviceAccountKey)
|
|
||||||
assert.Equal("email", client.serviceAccount)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTerminateServiceAccount(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
iamAPI iamAPI
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"delete works": {
|
|
||||||
iamAPI: stubIAMAPI{},
|
|
||||||
},
|
|
||||||
"delete fails": {
|
|
||||||
iamAPI: stubIAMAPI{
|
|
||||||
deleteServiceAccountErr: errors.New("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",
|
|
||||||
serviceAccount: "service-account",
|
|
||||||
iamAPI: tc.iamAPI,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.TerminateServiceAccount(ctx)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -162,8 +162,8 @@ type GCPConfig struct {
|
||||||
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types
|
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types
|
||||||
StateDiskType string `yaml:"stateDiskType" validate:"oneof=pd-standard pd-balanced pd-ssd"`
|
StateDiskType string `yaml:"stateDiskType" validate:"oneof=pd-standard pd-balanced pd-ssd"`
|
||||||
// description: |
|
// description: |
|
||||||
// Roles added to service account.
|
// Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/latest/#/getting-started/install?id=authorization
|
||||||
ServiceAccountRoles []string `yaml:"serviceAccountRoles"`
|
ServiceAccountKeyPath string `yaml:"serviceAccountKeyPath"`
|
||||||
// description: |
|
// description: |
|
||||||
// Expected confidential VM measurements.
|
// Expected confidential VM measurements.
|
||||||
Measurements Measurements `yaml:"measurements"`
|
Measurements Measurements `yaml:"measurements"`
|
||||||
|
@ -232,20 +232,14 @@ func Default() *Config {
|
||||||
EnforcedMeasurements: []uint32{8, 9, 11, 12},
|
EnforcedMeasurements: []uint32{8, 9, 11, 12},
|
||||||
},
|
},
|
||||||
GCP: &GCPConfig{
|
GCP: &GCPConfig{
|
||||||
Project: "",
|
Project: "",
|
||||||
Region: "",
|
Region: "",
|
||||||
Zone: "",
|
Zone: "",
|
||||||
Image: "projects/constellation-images/global/images/constellation-v1-5-0",
|
Image: "projects/constellation-images/global/images/constellation-v1-5-0",
|
||||||
ServiceAccountRoles: []string{
|
StateDiskType: "pd-ssd",
|
||||||
"roles/compute.instanceAdmin.v1",
|
ServiceAccountKeyPath: "serviceAccountKey.json",
|
||||||
"roles/compute.networkAdmin",
|
Measurements: copyPCRMap(gcpPCRs),
|
||||||
"roles/compute.securityAdmin",
|
EnforcedMeasurements: []uint32{0, 8, 9, 11, 12},
|
||||||
"roles/storage.admin",
|
|
||||||
"roles/iam.serviceAccountUser",
|
|
||||||
},
|
|
||||||
StateDiskType: "pd-ssd",
|
|
||||||
Measurements: copyPCRMap(gcpPCRs),
|
|
||||||
EnforcedMeasurements: []uint32{0, 8, 9, 11, 12},
|
|
||||||
},
|
},
|
||||||
QEMU: &QEMUConfig{
|
QEMU: &QEMUConfig{
|
||||||
Measurements: copyPCRMap(qemuPCRs),
|
Measurements: copyPCRMap(qemuPCRs),
|
||||||
|
|
|
@ -245,11 +245,11 @@ func init() {
|
||||||
GCPConfigDoc.Fields[4].Note = ""
|
GCPConfigDoc.Fields[4].Note = ""
|
||||||
GCPConfigDoc.Fields[4].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
|
GCPConfigDoc.Fields[4].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
|
||||||
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
|
GCPConfigDoc.Fields[4].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types"
|
||||||
GCPConfigDoc.Fields[5].Name = "serviceAccountRoles"
|
GCPConfigDoc.Fields[5].Name = "serviceAccountKeyPath"
|
||||||
GCPConfigDoc.Fields[5].Type = "[]string"
|
GCPConfigDoc.Fields[5].Type = "string"
|
||||||
GCPConfigDoc.Fields[5].Note = ""
|
GCPConfigDoc.Fields[5].Note = ""
|
||||||
GCPConfigDoc.Fields[5].Description = "Roles added to service account."
|
GCPConfigDoc.Fields[5].Description = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/latest/#/getting-started/install?id=authorization"
|
||||||
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Roles added to service account."
|
GCPConfigDoc.Fields[5].Comments[encoder.LineComment] = "Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/latest/#/getting-started/install?id=authorization"
|
||||||
GCPConfigDoc.Fields[6].Name = "measurements"
|
GCPConfigDoc.Fields[6].Name = "measurements"
|
||||||
GCPConfigDoc.Fields[6].Type = "Measurements"
|
GCPConfigDoc.Fields[6].Type = "Measurements"
|
||||||
GCPConfigDoc.Fields[6].Note = ""
|
GCPConfigDoc.Fields[6].Note = ""
|
||||||
|
|
|
@ -25,7 +25,6 @@ type ConstellationState struct {
|
||||||
GCPProject string `json:"gcpproject,omitempty"`
|
GCPProject string `json:"gcpproject,omitempty"`
|
||||||
GCPZone string `json:"gcpzone,omitempty"`
|
GCPZone string `json:"gcpzone,omitempty"`
|
||||||
GCPRegion string `json:"gcpregion,omitempty"`
|
GCPRegion string `json:"gcpregion,omitempty"`
|
||||||
GCPServiceAccount string `json:"gcpserviceaccount,omitempty"`
|
|
||||||
|
|
||||||
AzureWorkerInstances cloudtypes.Instances `json:"azureworkers,omitempty"`
|
AzureWorkerInstances cloudtypes.Instances `json:"azureworkers,omitempty"`
|
||||||
AzureControlPlaneInstances cloudtypes.Instances `json:"azurecontrolplanes,omitempty"`
|
AzureControlPlaneInstances cloudtypes.Instances `json:"azurecontrolplanes,omitempty"`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue