diff --git a/NOTICE b/NOTICE index 5e8f7c73..76f63a92 100644 --- a/NOTICE +++ b/NOTICE @@ -5,9 +5,9 @@ This project includes code licensed under the following terms: ---------- Module: cloud.google.com/go/artifactregistry -Version: v1.19.0 +Version: v1.20.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/artifactregistry/v1.19.0/artifactregistry/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/artifactregistry/v1.20.0/artifactregistry/LICENSE ---------- Module: cloud.google.com/go/auth @@ -95,9 +95,9 @@ License URL: https://github.com/clipperhouse/uax29/blob/v2.4.0/LICENSE ---------- Module: github.com/codesphere-cloud/cs-go -Version: v0.16.4 +Version: v0.17.0 License: Apache-2.0 -License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.16.4/LICENSE +License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.17.0/LICENSE ---------- Module: github.com/codesphere-cloud/oms/internal/tmpl @@ -179,9 +179,9 @@ License URL: https://github.com/googleapis/enterprise-certificate-proxy/blob/v0. ---------- Module: github.com/googleapis/gax-go/v2 -Version: v2.16.0 +Version: v2.17.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/gax-go/blob/v2.16.0/v2/LICENSE +License URL: https://github.com/googleapis/gax-go/blob/v2.17.0/v2/LICENSE ---------- Module: github.com/hashicorp/go-cleanhttp @@ -353,21 +353,21 @@ License URL: https://github.com/yaml/go-yaml/blob/v3.0.4/LICENSE ---------- Module: golang.org/x/crypto -Version: v0.47.0 +Version: v0.48.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/crypto/+/v0.47.0:LICENSE +License URL: https://cs.opensource.google/go/x/crypto/+/v0.48.0:LICENSE ---------- Module: golang.org/x/net -Version: v0.49.0 +Version: v0.50.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/net/+/v0.49.0:LICENSE +License URL: https://cs.opensource.google/go/x/net/+/v0.50.0:LICENSE ---------- Module: golang.org/x/oauth2 -Version: v0.34.0 +Version: v0.35.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/oauth2/+/v0.34.0:LICENSE +License URL: https://cs.opensource.google/go/x/oauth2/+/v0.35.0:LICENSE ---------- Module: golang.org/x/sync/semaphore @@ -377,21 +377,21 @@ License URL: https://cs.opensource.google/go/x/sync/+/v0.19.0:LICENSE ---------- Module: golang.org/x/sys -Version: v0.40.0 +Version: v0.41.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/sys/+/v0.40.0:LICENSE +License URL: https://cs.opensource.google/go/x/sys/+/v0.41.0:LICENSE ---------- Module: golang.org/x/term -Version: v0.39.0 +Version: v0.40.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/term/+/v0.39.0:LICENSE +License URL: https://cs.opensource.google/go/x/term/+/v0.40.0:LICENSE ---------- Module: golang.org/x/text -Version: v0.33.0 +Version: v0.34.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/text/+/v0.33.0:LICENSE +License URL: https://cs.opensource.google/go/x/text/+/v0.34.0:LICENSE ---------- Module: golang.org/x/time/rate @@ -401,15 +401,15 @@ License URL: https://cs.opensource.google/go/x/time/+/v0.14.0:LICENSE ---------- Module: google.golang.org/api -Version: v0.264.0 +Version: v0.266.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.264.0/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.266.0/LICENSE ---------- Module: google.golang.org/api/internal/third_party/uritemplates -Version: v0.264.0 +Version: v0.266.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.264.0/internal/third_party/uritemplates/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.266.0/internal/third_party/uritemplates/LICENSE ---------- Module: google.golang.org/genproto/googleapis @@ -419,21 +419,21 @@ License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/LICENSE ---------- Module: google.golang.org/genproto/googleapis/api -Version: v0.0.0-20260128011058-8636f8732409 +Version: v0.0.0-20260203192932-546029d2fa20 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/googleapis/api/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/546029d2fa20/googleapis/api/LICENSE ---------- Module: google.golang.org/genproto/googleapis/rpc -Version: v0.0.0-20260128011058-8636f8732409 +Version: v0.0.0-20260203192932-546029d2fa20 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/googleapis/rpc/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/546029d2fa20/googleapis/rpc/LICENSE ---------- Module: google.golang.org/grpc -Version: v1.78.0 +Version: v1.79.1 License: Apache-2.0 -License URL: https://github.com/grpc/grpc-go/blob/v1.78.0/LICENSE +License URL: https://github.com/grpc/grpc-go/blob/v1.79.1/LICENSE ---------- Module: google.golang.org/protobuf diff --git a/cli/cmd/bootstrap_gcp.go b/cli/cmd/bootstrap_gcp.go index 05c3c2e8..91378e0c 100644 --- a/cli/cmd/bootstrap_gcp.go +++ b/cli/cmd/bootstrap_gcp.go @@ -62,6 +62,8 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.BaseDomain, "base-domain", "", "Base domain for Codesphere (required)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GithubAppClientID, "github-app-client-id", "", "Github App Client ID (required)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GithubAppClientSecret, "github-app-client-secret", "", "Github App Client Secret (required)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubPAT, "github-pat", "", "GitHub Personal Access Token to use for direct image access. Scope required: package read (optional)") + flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubAppName, "github-app-name", "", "Github App Name (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.SecretsDir, "secrets-dir", "/etc/codesphere/secrets", "Directory for secrets (default: /etc/codesphere/secrets)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.FolderID, "folder-id", "", "GCP Folder ID (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.SSHPublicKeyPath, "ssh-public-key-path", "~/.ssh/id_rsa.pub", "SSH Public Key Path (default: ~/.ssh/id_rsa.pub)") @@ -74,16 +76,13 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.Region, "region", "europe-west4", "GCP Region (default: europe-west4)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.Zone, "zone", "europe-west4-a", "GCP Zone (default: europe-west4-a)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.DNSProjectID, "dns-project-id", "", "GCP Project ID for Cloud DNS (optional)") - flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.DNSProjectServiceAccount, "dns-project-sa", "", "GCP Project Service Account for Cloud DNS (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.DNSZoneName, "dns-zone-name", "oms-testing", "Cloud DNS Zone Name (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.InstallVersion, "install-version", "", "Codesphere version to install (default: none)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.InstallHash, "install-hash", "", "Codesphere package hash to install (default: none)") flags.StringArrayVarP(&bootstrapGcpCmd.CodesphereEnv.InstallSkipSteps, "install-skip-steps", "s", []string{}, "Installation steps to skip during Codesphere installation (optional)") - flags.StringVar(&bootstrapGcpCmd.InputRegistryType, "registry-type", "local-container", "Container registry type to use (options: local-container, artifact-registry) (default: artifact-registry)") flags.BoolVar(&bootstrapGcpCmd.CodesphereEnv.WriteConfig, "write-config", true, "Write generated install config to file (default: true)") flags.BoolVar(&bootstrapGcpCmd.SSHQuiet, "ssh-quiet", true, "Suppress SSH command output (default: true)") - flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.GitHubPAT, "github-pat", "", "GitHub Personal Access Token to use for direct image access. Scope required: package read (optional)") flags.StringVar(&bootstrapGcpCmd.CodesphereEnv.RegistryUser, "registry-user", "", "Custom Registry username (only for GitHub registry type) (optional)") flags.StringArrayVar(&bootstrapGcpCmd.CodesphereEnv.Experiments, "experiments", gcp.DefaultExperiments, "Experiments to enable in Codesphere installation (optional)") flags.StringArrayVar(&bootstrapGcpCmd.CodesphereEnv.FeatureFlags, "feature-flags", []string{}, "Feature flags to enable in Codesphere installation (optional)") diff --git a/docs/oms-cli_beta_bootstrap-gcp.md b/docs/oms-cli_beta_bootstrap-gcp.md index d9c83f67..09d3ca90 100644 --- a/docs/oms-cli_beta_bootstrap-gcp.md +++ b/docs/oms-cli_beta_bootstrap-gcp.md @@ -22,13 +22,13 @@ oms-cli beta bootstrap-gcp [flags] --custom-pg-ip string Custom PostgreSQL IP (optional) --datacenter-id int Datacenter ID (default: 1) (default 1) --dns-project-id string GCP Project ID for Cloud DNS (optional) - --dns-project-sa string GCP Project Service Account for Cloud DNS (optional) --dns-zone-name string Cloud DNS Zone Name (optional) (default "oms-testing") --experiments stringArray Experiments to enable in Codesphere installation (optional) (default [managed-services,vcluster,custom-service-image,ms-in-ls,secret-management,sub-path-mount]) --feature-flags stringArray Feature flags to enable in Codesphere installation (optional) --folder-id string GCP Folder ID (optional) --github-app-client-id string Github App Client ID (required) --github-app-client-secret string Github App Client Secret (required) + --github-app-name string Github App Name (optional) --github-pat string GitHub Personal Access Token to use for direct image access. Scope required: package read (optional) -h, --help help for bootstrap-gcp --install-config string Path to install config file (optional) (default "config.yaml") diff --git a/internal/bootstrap/gcp/gcp.go b/internal/bootstrap/gcp/gcp.go index 66372dd6..e3cb8c8a 100644 --- a/internal/bootstrap/gcp/gcp.go +++ b/internal/bootstrap/gcp/gcp.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "slices" "sort" "strings" "sync" @@ -78,28 +79,28 @@ type GCPBootstrapper struct { } type CodesphereEnvironment struct { - ProjectID string `json:"project_id"` - ProjectName string `json:"project_name"` - DNSProjectID string `json:"dns_project_id"` - DNSProjectServiceAccount string `json:"dns_project_service_account"` - Jumpbox *node.Node `json:"jumpbox"` - PostgreSQLNode *node.Node `json:"postgres_node"` - ControlPlaneNodes []*node.Node `json:"control_plane_nodes"` - CephNodes []*node.Node `json:"ceph_nodes"` - ContainerRegistryURL string `json:"-"` - ExistingConfigUsed bool `json:"-"` - InstallVersion string `json:"install_version"` - InstallHash string `json:"install_hash"` - InstallSkipSteps []string `json:"install_skip_steps"` - Preemptible bool `json:"preemptible"` - WriteConfig bool `json:"-"` - GatewayIP string `json:"gateway_ip"` - PublicGatewayIP string `json:"public_gateway_ip"` - RegistryType RegistryType `json:"registry_type"` - GitHubPAT string `json:"-"` - RegistryUser string `json:"-"` - Experiments []string `json:"experiments"` - FeatureFlags []string `json:"feature_flags"` + ProjectID string `json:"project_id"` + ProjectName string `json:"project_name"` + DNSProjectID string `json:"dns_project_id"` + Jumpbox *node.Node `json:"jumpbox"` + PostgreSQLNode *node.Node `json:"postgres_node"` + ControlPlaneNodes []*node.Node `json:"control_plane_nodes"` + CephNodes []*node.Node `json:"ceph_nodes"` + ContainerRegistryURL string `json:"-"` + ExistingConfigUsed bool `json:"-"` + InstallVersion string `json:"install_version"` + InstallHash string `json:"install_hash"` + InstallSkipSteps []string `json:"install_skip_steps"` + Preemptible bool `json:"preemptible"` + WriteConfig bool `json:"-"` + GatewayIP string `json:"gateway_ip"` + PublicGatewayIP string `json:"public_gateway_ip"` + RegistryType RegistryType `json:"registry_type"` + GitHubPAT string `json:"-"` + GitHubAppName string `json:"-"` + RegistryUser string `json:"-"` + Experiments []string `json:"experiments"` + FeatureFlags []string `json:"feature_flags"` // Config InstallConfigPath string `json:"-"` @@ -154,9 +155,9 @@ func GetInfraFilePath() string { func (b *GCPBootstrapper) Bootstrap() error { if b.Env.InstallVersion != "" { - err := b.stlog.Step("Validate package to install", b.ValidatePackageName) + err := b.stlog.Step("Validate input", b.ValidateInput) if err != nil { - return fmt.Errorf("invalid package name: %w", err) + return fmt.Errorf("invalid input: %w", err) } } @@ -293,7 +294,7 @@ func (b *GCPBootstrapper) Bootstrap() error { return nil } -func (b *GCPBootstrapper) ValidatePackageName() error { +func (b *GCPBootstrapper) ValidateInput() error { build, err := b.PortalClient.GetBuild(portal.CodesphereProduct, b.Env.InstallVersion, b.Env.InstallHash) if err != nil { return fmt.Errorf("failed to get codesphere package: %w", err) @@ -312,6 +313,11 @@ func (b *GCPBootstrapper) ValidatePackageName() error { } } + ghParams := []string{b.Env.GitHubAppName, b.Env.GithubAppClientID, b.Env.GithubAppClientSecret} + if slices.Contains(ghParams, "") && strings.Join(ghParams, "") != "" { + return fmt.Errorf("GitHub app credentials are not fully specified (all or none of GitHubAppName, GithubAppClientID, GithubAppClientSecret must be set)") + } + return fmt.Errorf("specified package does not contain required installer artifact %s. Existing artifacts: %s", requiredFilename, strings.Join(filenames, ", ")) } @@ -467,7 +473,7 @@ func (b *GCPBootstrapper) EnsureServiceAccounts() error { } func (b *GCPBootstrapper) EnsureIAMRoles() error { - err := b.ensureIAMRoleWithRetry("cloud-controller", []string{"roles/compute.admin"}) + err := b.ensureIAMRoleWithRetry(b.Env.ProjectID, "cloud-controller", b.Env.ProjectID, []string{"roles/compute.admin"}) if err != nil { return err } @@ -481,14 +487,14 @@ func (b *GCPBootstrapper) EnsureIAMRoles() error { return nil } - err = b.ensureIAMRoleWithRetry("artifact-registry-writer", []string{"roles/artifactregistry.writer"}) + err = b.ensureIAMRoleWithRetry(b.Env.ProjectID, "artifact-registry-writer", b.Env.ProjectID, []string{"roles/artifactregistry.writer"}) return err } -func (b *GCPBootstrapper) ensureIAMRoleWithRetry(serviceAccount string, roles []string) error { +func (b *GCPBootstrapper) ensureIAMRoleWithRetry(projectID string, serviceAccount string, serviceAccountProjectID string, roles []string) error { var err error for retries := range 5 { - err = b.GCPClient.AssignIAMRole(b.Env.ProjectID, serviceAccount, roles) + err = b.GCPClient.AssignIAMRole(projectID, serviceAccount, serviceAccountProjectID, roles) if err == nil { return nil } @@ -501,17 +507,11 @@ func (b *GCPBootstrapper) ensureIAMRoleWithRetry(serviceAccount string, roles [] } func (b *GCPBootstrapper) ensureDnsPermissions() error { - if b.Env.DNSProjectID != "" { - if b.Env.DNSProjectServiceAccount == "" { - return errors.New("dns project service account with role roles/dns.admin must be provided when dns project id is set") - } - err := b.GCPClient.GrantImpersonation("cloud-controller", b.Env.ProjectID, b.Env.DNSProjectServiceAccount, b.Env.DNSProjectID) - if err != nil { - return fmt.Errorf("failed to grant impersonization on dns project %s to cloud-controller service account: %w", b.Env.DNSProjectID, err) - } - return nil + dnsProject := b.Env.DNSProjectID + if b.Env.DNSProjectID == "" { + dnsProject = b.Env.ProjectID } - err := b.ensureIAMRoleWithRetry("cloud-controller", []string{"roles/dns.admin"}) + err := b.ensureIAMRoleWithRetry(dnsProject, "cloud-controller", b.Env.ProjectID, []string{"roles/dns.admin"}) if err != nil { return err } @@ -1218,8 +1218,10 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { }, }, } - b.Env.InstallConfig.Codesphere.GitProviders = &files.GitProvidersConfig{ - GitHub: &files.GitProviderConfig{ + + b.Env.InstallConfig.Codesphere.GitProviders = &files.GitProvidersConfig{} + if b.Env.GitHubAppName != "" && b.Env.GithubAppClientID != "" && b.Env.GithubAppClientSecret != "" { + b.Env.InstallConfig.Codesphere.GitProviders.GitHub = &files.GitProviderConfig{ Enabled: true, URL: "https://github.com", API: files.APIConfig{ @@ -1229,11 +1231,14 @@ func (b *GCPBootstrapper) UpdateInstallConfig() error { Issuer: "https://github.com", AuthorizationEndpoint: "https://github.com/login/oauth/authorize", TokenEndpoint: "https://github.com/login/oauth/access_token", + ClientAuthMethod: "client_secret_post", + RedirectURI: "https://cs." + b.Env.BaseDomain + "/ide/auth/github/callback", + InstallationURI: "https://github.com/apps/" + b.Env.GitHubAppName + "/installations/new", ClientID: b.Env.GithubAppClientID, ClientSecret: b.Env.GithubAppClientSecret, }, - }, + } } b.Env.InstallConfig.Codesphere.Experiments = b.Env.Experiments b.Env.InstallConfig.Codesphere.Features = b.Env.FeatureFlags diff --git a/internal/bootstrap/gcp/gcp_client.go b/internal/bootstrap/gcp/gcp_client.go index 912dfc9b..b487343a 100644 --- a/internal/bootstrap/gcp/gcp_client.go +++ b/internal/bootstrap/gcp/gcp_client.go @@ -43,8 +43,7 @@ type GCPClientManager interface { CreateArtifactRegistry(projectID, region, repoName string) (*artifactpb.Repository, error) CreateServiceAccount(projectID, name, displayName string) (string, bool, error) CreateServiceAccountKey(projectID, saEmail string) (string, error) - AssignIAMRole(saProjectID, saEmail string, roles []string) error - GrantImpersonation(impersonatingServiceAccount, impersonatingProjectID, imperonatedServiceAccount, impersonatedProjectID string) error + AssignIAMRole(projectID, saEmail string, saProjectID string, roles []string) error CreateVPC(projectID, region, networkName, subnetName, routerName, natName string) error CreateFirewallRule(projectID string, rule *computepb.Firewall) error CreateInstance(projectID, zone string, instance *computepb.Instance) error @@ -318,14 +317,13 @@ func (c *GCPClient) CreateServiceAccountKey(projectID, saEmail string) (string, } // AssignIAMRole assigns the specified IAM role to a service account in a project. -func (c *GCPClient) AssignIAMRole(projectID, saName string, roles []string) error { - saEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", saName, projectID) +func (c *GCPClient) AssignIAMRole(projectID, saName string, saProjectID string, roles []string) error { + saEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", saName, saProjectID) member := fmt.Sprintf("serviceAccount:%s", saEmail) resource := fmt.Sprintf("projects/%s", projectID) return c.addRoleBindingToProject(member, roles, resource) } -// Types between ServiceAccount and Project IAM API differ, so we need a separate function func (c *GCPClient) addRoleBindingToProject(member string, roles []string, resource string) error { client, err := resourcemanager.NewProjectsClient(c.ctx) if err != nil { @@ -380,73 +378,6 @@ func (c *GCPClient) addRoleBindingToProject(member string, roles []string, resou return err } -// Types between ServiceAccount and Project IAM API differ, so we need a separate function -func (c *GCPClient) addRoleBindingToServiceAccount(member string, roles []string, resource string) error { - iamService, err := iam.NewService(c.ctx) - if err != nil { - return err - } - - // Get current policy - policy, err := iamService.Projects.ServiceAccounts.GetIamPolicy(resource).Context(c.ctx).Do() - if err != nil { - return fmt.Errorf("failed to get IAM policy for service account: %w", err) - } - - // Add role bindings directly to iam.Policy - updated := false - for _, role := range roles { - bindingExists := false - for _, binding := range policy.Bindings { - if binding.Role == role { - if !slices.Contains(binding.Members, member) { - binding.Members = append(binding.Members, member) - updated = true - } - bindingExists = true - break - } - } - if bindingExists { - continue - } - - // Assign role - policy.Bindings = append(policy.Bindings, &iam.Binding{ - Role: role, - Members: []string{member}, - }) - updated = true - } - - if !updated { - return nil - } - - // Set the updated policy - setReq := &iam.SetIamPolicyRequest{ - Policy: policy, - } - _, err = iamService.Projects.ServiceAccounts.SetIamPolicy(resource, setReq).Context(c.ctx).Do() - if err != nil { - return fmt.Errorf("failed to set IAM policy for service account: %w", err) - } - - return nil -} - -// GrantImpersonation grants the "roles/iam.serviceAccountTokenCreator" role to the impersonating service account on the impersonated service account, -// allowing the impersonating service account to generate access tokens for the impersonated service account, which is necessary for cross-project impersonation. -func (c *GCPClient) GrantImpersonation(impersonatingServiceAccount, impersonatingProjectID, impersonatedServiceAccount, impersonatedProjectID string) error { - impersonatingSAEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", impersonatingServiceAccount, impersonatingProjectID) - impersonatedSAEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", impersonatedServiceAccount, impersonatedProjectID) - - resourceName := fmt.Sprintf("projects/%s/serviceAccounts/%s", impersonatedProjectID, impersonatedSAEmail) - member := fmt.Sprintf("serviceAccount:%s", impersonatingSAEmail) - - return c.addRoleBindingToServiceAccount(member, []string{"roles/iam.serviceAccountTokenCreator"}, resourceName) -} - // CreateVPC creates a VPC network with the specified subnet, router, and NAT gateway. func (c *GCPClient) CreateVPC(projectID, region, networkName, subnetName, routerName, natName string) error { // Create Network diff --git a/internal/bootstrap/gcp/gcp_test.go b/internal/bootstrap/gcp/gcp_test.go index c81ef7db..cf230ff6 100644 --- a/internal/bootstrap/gcp/gcp_test.go +++ b/internal/bootstrap/gcp/gcp_test.go @@ -67,22 +67,24 @@ var _ = Describe("GCP Bootstrapper", func() { stlog = bootstrap.NewStepLogger(false) csEnv = &gcp.CodesphereEnvironment{ - InstallConfigPath: "fake-config-file", - SecretsFilePath: "fake-secret", - ProjectName: "test-project", - SecretsDir: "/etc/codesphere/secrets", - BillingAccount: "test-billing-account", - Region: "us-central1", - Zone: "us-central1-a", - DatacenterID: 1, - BaseDomain: "example.com", - DNSProjectID: "dns-project", - DNSProjectServiceAccount: "dns-admin", - DNSZoneName: "test-zone", - SSHPublicKeyPath: "key.pub", - ProjectID: "pid", - Experiments: gcp.DefaultExperiments, - FeatureFlags: []string{}, + GitHubAppName: "fake-app", + GithubAppClientID: "fake-client-id", + GithubAppClientSecret: "fake-secret", + InstallConfigPath: "fake-config-file", + SecretsFilePath: "fake-secret", + ProjectName: "test-project", + SecretsDir: "/etc/codesphere/secrets", + BillingAccount: "test-billing-account", + Region: "us-central1", + Zone: "us-central1-a", + DatacenterID: 1, + BaseDomain: "example.com", + DNSProjectID: "dns-project", + DNSZoneName: "test-zone", + SSHPublicKeyPath: "key.pub", + ProjectID: "pid", + Experiments: gcp.DefaultExperiments, + FeatureFlags: []string{}, InstallConfig: &files.RootConfig{ Registry: &files.RegistryConfig{}, Postgres: files.PostgresConfig{ @@ -111,6 +113,7 @@ var _ = Describe("GCP Bootstrapper", func() { csEnv.CephNodes = []*node.Node{} csEnv.PostgreSQLNode = nil csEnv.Jumpbox = nil + csEnv.ProjectID = "" }) It("runs bootstrap successfully", func() { @@ -127,44 +130,46 @@ var _ = Describe("GCP Bootstrapper", func() { return realIcm.GetInstallConfig() }) + projectId := "test-project-12345" + // 2. EnsureSecrets fw.EXPECT().Exists("fake-secret").Return(false) icg.EXPECT().GetVault().Return(&files.InstallVault{}) // 3. EnsureProject gc.EXPECT().GetProjectByName(mock.Anything, "test-project").Return(nil, fmt.Errorf("project not found: test-project")) - gc.EXPECT().CreateProjectID("test-project").Return("test-project-id") + gc.EXPECT().CreateProjectID("test-project").Return(projectId) gc.EXPECT().CreateProject(mock.Anything, mock.Anything, "test-project").Return(mock.Anything, nil) // 4. EnsureBilling - gc.EXPECT().GetBillingInfo("test-project-id").Return(&cloudbilling.ProjectBillingInfo{BillingEnabled: false}, nil) - gc.EXPECT().EnableBilling("test-project-id", "test-billing-account").Return(nil) + gc.EXPECT().GetBillingInfo(projectId).Return(&cloudbilling.ProjectBillingInfo{BillingEnabled: false}, nil) + gc.EXPECT().EnableBilling(projectId, "test-billing-account").Return(nil) // 5. EnsureAPIsEnabled - gc.EXPECT().EnableAPIs("test-project-id", mock.Anything).Return(nil) + gc.EXPECT().EnableAPIs(projectId, mock.Anything).Return(nil) // 6. EnsureArtifactRegistry - gc.EXPECT().GetArtifactRegistry("test-project-id", "us-central1", "codesphere-registry").Return(nil, fmt.Errorf("not found")) - gc.EXPECT().CreateArtifactRegistry("test-project-id", "us-central1", "codesphere-registry").Return(&artifactregistrypb.Repository{Name: "codesphere-registry"}, nil) + gc.EXPECT().GetArtifactRegistry(projectId, "us-central1", "codesphere-registry").Return(nil, fmt.Errorf("not found")) + gc.EXPECT().CreateArtifactRegistry(projectId, "us-central1", "codesphere-registry").Return(&artifactregistrypb.Repository{Name: "codesphere-registry"}, nil) // 7. EnsureServiceAccounts - gc.EXPECT().CreateServiceAccount("test-project-id", "cloud-controller", "cloud-controller").Return("cloud-controller@p.iam.gserviceaccount.com", false, nil) - gc.EXPECT().CreateServiceAccount("test-project-id", "artifact-registry-writer", "artifact-registry-writer").Return("writer@p.iam.gserviceaccount.com", true, nil) - gc.EXPECT().CreateServiceAccountKey("test-project-id", "writer@p.iam.gserviceaccount.com").Return("fake-key", nil) + gc.EXPECT().CreateServiceAccount(projectId, "cloud-controller", "cloud-controller").Return("cloud-controller@p.iam.gserviceaccount.com", false, nil) + gc.EXPECT().CreateServiceAccount(projectId, "artifact-registry-writer", "artifact-registry-writer").Return("writer@p.iam.gserviceaccount.com", true, nil) + gc.EXPECT().CreateServiceAccountKey(projectId, "writer@p.iam.gserviceaccount.com").Return("fake-key", nil) // 8. EnsureIAMRoles - gc.EXPECT().AssignIAMRole("test-project-id", "artifact-registry-writer", []string{"roles/artifactregistry.writer"}).Return(nil) - gc.EXPECT().AssignIAMRole("test-project-id", "cloud-controller", []string{"roles/compute.admin"}).Return(nil) - gc.EXPECT().GrantImpersonation("cloud-controller", "test-project-id", bs.Env.DNSProjectServiceAccount, bs.Env.DNSProjectID).Return(nil) + gc.EXPECT().AssignIAMRole(projectId, "artifact-registry-writer", projectId, []string{"roles/artifactregistry.writer"}).Return(nil) + gc.EXPECT().AssignIAMRole(projectId, "cloud-controller", projectId, []string{"roles/compute.admin"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.DNSProjectID, "cloud-controller", projectId, []string{"roles/dns.admin"}).Return(nil) // 9. EnsureVPC - gc.EXPECT().CreateVPC("test-project-id", "us-central1", "test-project-id-vpc", "test-project-id-us-central1-subnet", "test-project-id-router", "test-project-id-nat-gateway").Return(nil) + gc.EXPECT().CreateVPC(projectId, "us-central1", projectId+"-vpc", projectId+"-us-central1-subnet", projectId+"-router", projectId+"-nat-gateway").Return(nil) // 10. EnsureFirewallRules (5 times) - gc.EXPECT().CreateFirewallRule("test-project-id", mock.Anything).Return(nil).Times(5) + gc.EXPECT().CreateFirewallRule(projectId, mock.Anything).Return(nil).Times(5) // 11. EnsureComputeInstances - gc.EXPECT().CreateInstance("test-project-id", "us-central1-a", mock.Anything).Return(nil).Times(9) + gc.EXPECT().CreateInstance(projectId, "us-central1-a", mock.Anything).Return(nil).Times(9) // GetInstance calls to retrieve IPs ipResp := &computepb.Instance{ NetworkInterfaces: []*computepb.NetworkInterface{ @@ -177,16 +182,16 @@ var _ = Describe("GCP Bootstrapper", func() { }, } - gc.EXPECT().GetInstance("test-project-id", "us-central1-a", mock.Anything).Return(ipResp, nil).Times(9) + gc.EXPECT().GetInstance(projectId, "us-central1-a", mock.Anything).Return(ipResp, nil).Times(9) fw.EXPECT().ReadFile(mock.Anything).Return([]byte("fake-key"), nil).Times(9) // 12. EnsureGatewayIPAddresses - gc.EXPECT().GetAddress("test-project-id", "us-central1", "gateway").Return(nil, fmt.Errorf("not found")) - gc.EXPECT().CreateAddress("test-project-id", "us-central1", mock.MatchedBy(func(addr *computepb.Address) bool { return *addr.Name == "gateway" })).Return("1.1.1.1", nil) - gc.EXPECT().GetAddress("test-project-id", "us-central1", "gateway").Return(nil, fmt.Errorf("not found")) - gc.EXPECT().GetAddress("test-project-id", "us-central1", "public-gateway").Return(nil, fmt.Errorf("not found")) - gc.EXPECT().CreateAddress("test-project-id", "us-central1", mock.MatchedBy(func(addr *computepb.Address) bool { return *addr.Name == "public-gateway" })).Return("2.2.2.2", nil) - gc.EXPECT().GetAddress("test-project-id", "us-central1", "public-gateway").Return(&computepb.Address{Address: protoString("2.2.2.2")}, nil) + gc.EXPECT().GetAddress(projectId, "us-central1", "gateway").Return(nil, fmt.Errorf("not found")) + gc.EXPECT().CreateAddress(projectId, "us-central1", mock.MatchedBy(func(addr *computepb.Address) bool { return *addr.Name == "gateway" })).Return("1.1.1.1", nil) + gc.EXPECT().GetAddress(projectId, "us-central1", "gateway").Return(nil, fmt.Errorf("not found")) + gc.EXPECT().GetAddress(projectId, "us-central1", "public-gateway").Return(nil, fmt.Errorf("not found")) + gc.EXPECT().CreateAddress(projectId, "us-central1", mock.MatchedBy(func(addr *computepb.Address) bool { return *addr.Name == "public-gateway" })).Return("2.2.2.2", nil) + gc.EXPECT().GetAddress(projectId, "us-central1", "public-gateway").Return(&computepb.Address{Address: protoString("2.2.2.2")}, nil) // 16. UpdateInstallConfig icg.EXPECT().GenerateSecrets().Return(nil) @@ -210,8 +215,8 @@ var _ = Describe("GCP Bootstrapper", func() { })).Return(nil) // 19. EnsureDNSRecords - gc.EXPECT().EnsureDNSManagedZone("dns-project", "test-zone", "example.com.", mock.Anything).Return(nil) - gc.EXPECT().EnsureDNSRecordSets("dns-project", "test-zone", mock.MatchedBy(func(records []*dns.ResourceRecordSet) bool { + gc.EXPECT().EnsureDNSManagedZone(csEnv.DNSProjectID, "test-zone", "example.com.", mock.Anything).Return(nil) + gc.EXPECT().EnsureDNSRecordSets(csEnv.DNSProjectID, "test-zone", mock.MatchedBy(func(records []*dns.ResourceRecordSet) bool { return len(records) == 4 })).Return(nil) @@ -254,7 +259,7 @@ var _ = Describe("GCP Bootstrapper", func() { }) }) - Describe("ValidatePackageName", func() { + Describe("ValidateInput", func() { var ( artifacts []portal.Artifact ) @@ -274,13 +279,23 @@ var _ = Describe("GCP Bootstrapper", func() { }, nil) }) + Context("when GitHub arguments are partially set", func() { + BeforeEach(func() { + csEnv.GitHubAppName = "" + }) + It("fails", func() { + err := bs.ValidateInput() + Expect(err).To(MatchError(MatchRegexp("GitHub app credentials are not fully specified"))) + }) + }) + Context("when GHCR registry is used", func() { BeforeEach(func() { csEnv.RegistryType = gcp.RegistryTypeGitHub }) It("succeeds when package exists and has the lite package", func() { - err := bs.ValidatePackageName() + err := bs.ValidateInput() Expect(err).NotTo(HaveOccurred()) }) @@ -289,7 +304,7 @@ var _ = Describe("GCP Bootstrapper", func() { artifacts[0].Filename = "installer.tar.gz" }) It("fails", func() { - err := bs.ValidatePackageName() + err := bs.ValidateInput() Expect(err).To(MatchError(MatchRegexp("artifact installer-lite\\.tar\\.gz"))) }) }) @@ -305,7 +320,7 @@ var _ = Describe("GCP Bootstrapper", func() { artifacts[0].Filename = "installer.tar.gz" }) It("succeeds", func() { - err := bs.ValidatePackageName() + err := bs.ValidateInput() Expect(err).NotTo(HaveOccurred()) }) }) @@ -315,7 +330,7 @@ var _ = Describe("GCP Bootstrapper", func() { artifacts[0].Filename = "installer-lite.tar.gz" }) It("fails", func() { - err := bs.ValidatePackageName() + err := bs.ValidateInput() Expect(err).To(MatchError(MatchRegexp("artifact installer\\.tar\\.gz"))) }) }) @@ -781,9 +796,9 @@ var _ = Describe("GCP Bootstrapper", func() { csEnv.RegistryType = gcp.RegistryTypeArtifactRegistry }) It("assigns roles correctly", func() { - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", []string{"roles/compute.admin"}).Return(nil) - gc.EXPECT().GrantImpersonation("cloud-controller", bs.Env.ProjectID, bs.Env.DNSProjectServiceAccount, bs.Env.DNSProjectID).Return(nil) - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "artifact-registry-writer", []string{"roles/artifactregistry.writer"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", csEnv.ProjectID, []string{"roles/compute.admin"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.DNSProjectID, "cloud-controller", csEnv.ProjectID, []string{"roles/dns.admin"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "artifact-registry-writer", csEnv.ProjectID, []string{"roles/artifactregistry.writer"}).Return(nil) err := bs.EnsureIAMRoles() Expect(err).NotTo(HaveOccurred()) @@ -794,9 +809,9 @@ var _ = Describe("GCP Bootstrapper", func() { csEnv.DNSProjectID = "" }) It("assigns DNS role to cloud-controller in main project", func() { - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", []string{"roles/compute.admin"}).Return(nil) - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", []string{"roles/dns.admin"}).Return(nil) - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "artifact-registry-writer", []string{"roles/artifactregistry.writer"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", csEnv.ProjectID, []string{"roles/compute.admin"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", csEnv.ProjectID, []string{"roles/dns.admin"}).Return(nil) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "artifact-registry-writer", csEnv.ProjectID, []string{"roles/artifactregistry.writer"}).Return(nil) err := bs.EnsureIAMRoles() Expect(err).NotTo(HaveOccurred()) @@ -806,24 +821,12 @@ var _ = Describe("GCP Bootstrapper", func() { Describe("Invalid cases", func() { It("fails when AssignIAMRole fails", func() { - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", []string{"roles/compute.admin"}).Return(fmt.Errorf("iam error")) + gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", csEnv.ProjectID, []string{"roles/compute.admin"}).Return(fmt.Errorf("iam error")) err := bs.EnsureIAMRoles() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("iam error")) }) - - Context("When DNS project is set, but DNS service account is unset", func() { - BeforeEach(func() { - csEnv.DNSProjectServiceAccount = "" - }) - It("fails", func() { - gc.EXPECT().AssignIAMRole(csEnv.ProjectID, "cloud-controller", []string{"roles/compute.admin"}).Return(nil) - - err := bs.EnsureIAMRoles() - Expect(err).To(MatchError(MatchRegexp("service account.*must be provided"))) - }) - }) }) }) @@ -1100,6 +1103,9 @@ var _ = Describe("GCP Bootstrapper", func() { }) Describe("UpdateInstallConfig", func() { + BeforeEach(func() { + csEnv.GitHubAppName = "fake-app-name" + }) Describe("Valid UpdateInstallConfig", func() { It("updates config and writes files", func() { icg.EXPECT().GenerateSecrets().Return(nil) @@ -1115,6 +1121,13 @@ var _ = Describe("GCP Bootstrapper", func() { Expect(bs.Env.InstallConfig.Codesphere.Domain).To(Equal("cs.example.com")) Expect(bs.Env.InstallConfig.Codesphere.Features).To(Equal([]string{})) Expect(bs.Env.InstallConfig.Codesphere.Experiments).To(Equal(gcp.DefaultExperiments)) + + expectedInstallURI := "https://github.com/apps/" + bs.Env.GitHubAppName + "/installations/new" + Expect(bs.Env.InstallConfig.Codesphere.GitProviders.GitHub.OAuth.InstallationURI).To(Equal(expectedInstallURI)) + expectedRedirectURI := "https://cs." + bs.Env.BaseDomain + "/ide/auth/github/callback" + Expect(bs.Env.InstallConfig.Codesphere.GitProviders.GitHub.OAuth.RedirectURI).To(Equal(expectedRedirectURI)) + Expect(bs.Env.InstallConfig.Codesphere.GitProviders.GitHub.OAuth.ClientAuthMethod).To(Equal("client_secret_post")) + issuers := bs.Env.InstallConfig.Cluster.Certificates.Override["issuers"].(map[string]interface{}) httpIssuer := issuers["letsEncryptHttp"].(map[string]interface{}) Expect(httpIssuer["enabled"]).To(Equal(true)) @@ -1159,6 +1172,24 @@ var _ = Describe("GCP Bootstrapper", func() { Expect(bs.Env.InstallConfig.Codesphere.Features).To(Equal([]string{"fake-flag1", "fake-flag2"})) }) }) + Context("When GitHub App name is not set ", func() { + BeforeEach(func() { + csEnv.GitHubAppName = "" + }) + It("skips setting GitHub OAuth configuration", func() { + icg.EXPECT().GenerateSecrets().Return(nil) + icg.EXPECT().WriteInstallConfig("fake-config-file", true).Return(nil) + icg.EXPECT().WriteVault("fake-secret", true).Return(nil) + + nodeClient.EXPECT().CopyFile(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() + + err := bs.UpdateInstallConfig() + Expect(err).NotTo(HaveOccurred()) + + Expect(bs.Env.InstallConfig.Codesphere.GitProviders.GitHub).To(BeNil()) + }) + + }) }) Describe("Invalid cases", func() { diff --git a/internal/bootstrap/gcp/mocks.go b/internal/bootstrap/gcp/mocks.go index 35022c09..495ad6e3 100644 --- a/internal/bootstrap/gcp/mocks.go +++ b/internal/bootstrap/gcp/mocks.go @@ -41,16 +41,16 @@ func (_m *MockGCPClientManager) EXPECT() *MockGCPClientManager_Expecter { } // AssignIAMRole provides a mock function for the type MockGCPClientManager -func (_mock *MockGCPClientManager) AssignIAMRole(saProjectID string, saEmail string, roles []string) error { - ret := _mock.Called(saProjectID, saEmail, roles) +func (_mock *MockGCPClientManager) AssignIAMRole(projectID string, saEmail string, saProjectID string, roles []string) error { + ret := _mock.Called(projectID, saEmail, saProjectID, roles) if len(ret) == 0 { panic("no return value specified for AssignIAMRole") } var r0 error - if returnFunc, ok := ret.Get(0).(func(string, string, []string) error); ok { - r0 = returnFunc(saProjectID, saEmail, roles) + if returnFunc, ok := ret.Get(0).(func(string, string, string, []string) error); ok { + r0 = returnFunc(projectID, saEmail, saProjectID, roles) } else { r0 = ret.Error(0) } @@ -63,14 +63,15 @@ type MockGCPClientManager_AssignIAMRole_Call struct { } // AssignIAMRole is a helper method to define mock.On call -// - saProjectID string +// - projectID string // - saEmail string +// - saProjectID string // - roles []string -func (_e *MockGCPClientManager_Expecter) AssignIAMRole(saProjectID interface{}, saEmail interface{}, roles interface{}) *MockGCPClientManager_AssignIAMRole_Call { - return &MockGCPClientManager_AssignIAMRole_Call{Call: _e.mock.On("AssignIAMRole", saProjectID, saEmail, roles)} +func (_e *MockGCPClientManager_Expecter) AssignIAMRole(projectID interface{}, saEmail interface{}, saProjectID interface{}, roles interface{}) *MockGCPClientManager_AssignIAMRole_Call { + return &MockGCPClientManager_AssignIAMRole_Call{Call: _e.mock.On("AssignIAMRole", projectID, saEmail, saProjectID, roles)} } -func (_c *MockGCPClientManager_AssignIAMRole_Call) Run(run func(saProjectID string, saEmail string, roles []string)) *MockGCPClientManager_AssignIAMRole_Call { +func (_c *MockGCPClientManager_AssignIAMRole_Call) Run(run func(projectID string, saEmail string, saProjectID string, roles []string)) *MockGCPClientManager_AssignIAMRole_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 string if args[0] != nil { @@ -80,14 +81,19 @@ func (_c *MockGCPClientManager_AssignIAMRole_Call) Run(run func(saProjectID stri if args[1] != nil { arg1 = args[1].(string) } - var arg2 []string + var arg2 string if args[2] != nil { - arg2 = args[2].([]string) + arg2 = args[2].(string) + } + var arg3 []string + if args[3] != nil { + arg3 = args[3].([]string) } run( arg0, arg1, arg2, + arg3, ) }) return _c @@ -98,7 +104,7 @@ func (_c *MockGCPClientManager_AssignIAMRole_Call) Return(err error) *MockGCPCli return _c } -func (_c *MockGCPClientManager_AssignIAMRole_Call) RunAndReturn(run func(saProjectID string, saEmail string, roles []string) error) *MockGCPClientManager_AssignIAMRole_Call { +func (_c *MockGCPClientManager_AssignIAMRole_Call) RunAndReturn(run func(projectID string, saEmail string, saProjectID string, roles []string) error) *MockGCPClientManager_AssignIAMRole_Call { _c.Call.Return(run) return _c } @@ -1314,72 +1320,3 @@ func (_c *MockGCPClientManager_GetProjectByName_Call) RunAndReturn(run func(fold _c.Call.Return(run) return _c } - -// GrantImpersonation provides a mock function for the type MockGCPClientManager -func (_mock *MockGCPClientManager) GrantImpersonation(impersonatingServiceAccount string, impersonatingProjectID string, imperonatedServiceAccount string, impersonatedProjectID string) error { - ret := _mock.Called(impersonatingServiceAccount, impersonatingProjectID, imperonatedServiceAccount, impersonatedProjectID) - - if len(ret) == 0 { - panic("no return value specified for GrantImpersonation") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(string, string, string, string) error); ok { - r0 = returnFunc(impersonatingServiceAccount, impersonatingProjectID, imperonatedServiceAccount, impersonatedProjectID) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockGCPClientManager_GrantImpersonation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GrantImpersonation' -type MockGCPClientManager_GrantImpersonation_Call struct { - *mock.Call -} - -// GrantImpersonation is a helper method to define mock.On call -// - impersonatingServiceAccount string -// - impersonatingProjectID string -// - imperonatedServiceAccount string -// - impersonatedProjectID string -func (_e *MockGCPClientManager_Expecter) GrantImpersonation(impersonatingServiceAccount interface{}, impersonatingProjectID interface{}, imperonatedServiceAccount interface{}, impersonatedProjectID interface{}) *MockGCPClientManager_GrantImpersonation_Call { - return &MockGCPClientManager_GrantImpersonation_Call{Call: _e.mock.On("GrantImpersonation", impersonatingServiceAccount, impersonatingProjectID, imperonatedServiceAccount, impersonatedProjectID)} -} - -func (_c *MockGCPClientManager_GrantImpersonation_Call) Run(run func(impersonatingServiceAccount string, impersonatingProjectID string, imperonatedServiceAccount string, impersonatedProjectID string)) *MockGCPClientManager_GrantImpersonation_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 string - if args[2] != nil { - arg2 = args[2].(string) - } - var arg3 string - if args[3] != nil { - arg3 = args[3].(string) - } - run( - arg0, - arg1, - arg2, - arg3, - ) - }) - return _c -} - -func (_c *MockGCPClientManager_GrantImpersonation_Call) Return(err error) *MockGCPClientManager_GrantImpersonation_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockGCPClientManager_GrantImpersonation_Call) RunAndReturn(run func(impersonatingServiceAccount string, impersonatingProjectID string, imperonatedServiceAccount string, impersonatedProjectID string) error) *MockGCPClientManager_GrantImpersonation_Call { - _c.Call.Return(run) - return _c -} diff --git a/internal/installer/files/config_yaml.go b/internal/installer/files/config_yaml.go index 006267c2..c3cb3e7f 100644 --- a/internal/installer/files/config_yaml.go +++ b/internal/installer/files/config_yaml.go @@ -408,6 +408,8 @@ type OAuthConfig struct { TokenEndpoint string `yaml:"tokenEndpoint"` ClientAuthMethod string `yaml:"clientAuthMethod,omitempty"` Scope string `yaml:"scope,omitempty"` + RedirectURI string `yaml:"redirectUri,omitempty"` + InstallationURI string `yaml:"installationUri,omitempty"` ClientID string `yaml:"-"` ClientSecret string `yaml:"-"` diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index 5e8f7c73..76f63a92 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -5,9 +5,9 @@ This project includes code licensed under the following terms: ---------- Module: cloud.google.com/go/artifactregistry -Version: v1.19.0 +Version: v1.20.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/artifactregistry/v1.19.0/artifactregistry/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/artifactregistry/v1.20.0/artifactregistry/LICENSE ---------- Module: cloud.google.com/go/auth @@ -95,9 +95,9 @@ License URL: https://github.com/clipperhouse/uax29/blob/v2.4.0/LICENSE ---------- Module: github.com/codesphere-cloud/cs-go -Version: v0.16.4 +Version: v0.17.0 License: Apache-2.0 -License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.16.4/LICENSE +License URL: https://github.com/codesphere-cloud/cs-go/blob/v0.17.0/LICENSE ---------- Module: github.com/codesphere-cloud/oms/internal/tmpl @@ -179,9 +179,9 @@ License URL: https://github.com/googleapis/enterprise-certificate-proxy/blob/v0. ---------- Module: github.com/googleapis/gax-go/v2 -Version: v2.16.0 +Version: v2.17.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/gax-go/blob/v2.16.0/v2/LICENSE +License URL: https://github.com/googleapis/gax-go/blob/v2.17.0/v2/LICENSE ---------- Module: github.com/hashicorp/go-cleanhttp @@ -353,21 +353,21 @@ License URL: https://github.com/yaml/go-yaml/blob/v3.0.4/LICENSE ---------- Module: golang.org/x/crypto -Version: v0.47.0 +Version: v0.48.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/crypto/+/v0.47.0:LICENSE +License URL: https://cs.opensource.google/go/x/crypto/+/v0.48.0:LICENSE ---------- Module: golang.org/x/net -Version: v0.49.0 +Version: v0.50.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/net/+/v0.49.0:LICENSE +License URL: https://cs.opensource.google/go/x/net/+/v0.50.0:LICENSE ---------- Module: golang.org/x/oauth2 -Version: v0.34.0 +Version: v0.35.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/oauth2/+/v0.34.0:LICENSE +License URL: https://cs.opensource.google/go/x/oauth2/+/v0.35.0:LICENSE ---------- Module: golang.org/x/sync/semaphore @@ -377,21 +377,21 @@ License URL: https://cs.opensource.google/go/x/sync/+/v0.19.0:LICENSE ---------- Module: golang.org/x/sys -Version: v0.40.0 +Version: v0.41.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/sys/+/v0.40.0:LICENSE +License URL: https://cs.opensource.google/go/x/sys/+/v0.41.0:LICENSE ---------- Module: golang.org/x/term -Version: v0.39.0 +Version: v0.40.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/term/+/v0.39.0:LICENSE +License URL: https://cs.opensource.google/go/x/term/+/v0.40.0:LICENSE ---------- Module: golang.org/x/text -Version: v0.33.0 +Version: v0.34.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/text/+/v0.33.0:LICENSE +License URL: https://cs.opensource.google/go/x/text/+/v0.34.0:LICENSE ---------- Module: golang.org/x/time/rate @@ -401,15 +401,15 @@ License URL: https://cs.opensource.google/go/x/time/+/v0.14.0:LICENSE ---------- Module: google.golang.org/api -Version: v0.264.0 +Version: v0.266.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.264.0/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.266.0/LICENSE ---------- Module: google.golang.org/api/internal/third_party/uritemplates -Version: v0.264.0 +Version: v0.266.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.264.0/internal/third_party/uritemplates/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.266.0/internal/third_party/uritemplates/LICENSE ---------- Module: google.golang.org/genproto/googleapis @@ -419,21 +419,21 @@ License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/LICENSE ---------- Module: google.golang.org/genproto/googleapis/api -Version: v0.0.0-20260128011058-8636f8732409 +Version: v0.0.0-20260203192932-546029d2fa20 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/googleapis/api/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/546029d2fa20/googleapis/api/LICENSE ---------- Module: google.golang.org/genproto/googleapis/rpc -Version: v0.0.0-20260128011058-8636f8732409 +Version: v0.0.0-20260203192932-546029d2fa20 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/8636f8732409/googleapis/rpc/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/546029d2fa20/googleapis/rpc/LICENSE ---------- Module: google.golang.org/grpc -Version: v1.78.0 +Version: v1.79.1 License: Apache-2.0 -License URL: https://github.com/grpc/grpc-go/blob/v1.78.0/LICENSE +License URL: https://github.com/grpc/grpc-go/blob/v1.79.1/LICENSE ---------- Module: google.golang.org/protobuf