diff --git a/internal/cloud/azure.go b/internal/cloud/azure.go index b20a731..8ca0738 100644 --- a/internal/cloud/azure.go +++ b/internal/cloud/azure.go @@ -346,13 +346,12 @@ func (p *azureProvider) ConnectCluster(ctx context.Context, clusterId, loadBalan return "", fmt.Errorf("failed to create Azure authorization client, %w", err) } azClient := clientFactory.NewRoleAssignmentsClient() - scope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", session.State.AzureProvider.SubscriptionID, session.State.AzureProvider.ResourceGroup) roleID := "4abbcc35-e782-43d8-92c5-2d3f1bd2253f" // Built-in "Azure Kubernetes Service Cluster User Role" role aksClusterUserRoleID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Authorization/roleDefinitions/%s", session.State.AzureProvider.SubscriptionID, roleID) - err = p.createRoleAssignmentWithRetries(ctx, azClient, scope, armauthorization.RoleAssignmentProperties{ + err = p.createRoleAssignmentWithRetries(ctx, azClient, clusterId, armauthorization.RoleAssignmentProperties{ PrincipalID: to.Ptr(session.State.AzureProvider.CreateCloudIdentity.ManagedIdentityPrincipalId), RoleDefinitionID: to.Ptr(aksClusterUserRoleID), PrincipalType: to.Ptr(armauthorization.PrincipalTypeServicePrincipal), @@ -425,7 +424,7 @@ func (p *azureProvider) ConnectCluster(ctx context.Context, clusterId, loadBalan } // Create AKS CLuster User Role Assignment for the group - err = p.createRoleAssignmentWithRetries(ctx, azClient, scope, armauthorization.RoleAssignmentProperties{ + err = p.createRoleAssignmentWithRetries(ctx, azClient, clusterId, armauthorization.RoleAssignmentProperties{ PrincipalID: to.Ptr(session.State.AzureProvider.ConnectCluster.EntraIDGroupId), RoleDefinitionID: to.Ptr(aksClusterUserRoleID), PrincipalType: to.Ptr(armauthorization.PrincipalTypeGroup), @@ -922,6 +921,16 @@ func (p *azureProvider) isMicrosoftEntraIDEnabled(ctx context.Context, cluster a } func (p *azureProvider) enableIdentityInCluster(ctx context.Context, opCluster armcontainerservice.ManagedCluster, resourceGroup, namespace, sa string) error { + if opCluster.Properties.SecurityProfile.WorkloadIdentity == nil { + return fmt.Errorf("cluster must have workload identity enabled") + } + if opCluster.Properties.EnableRBAC == nil { + return fmt.Errorf("cluster must have Azure RBAC enabled") + } + if opCluster.Properties.OidcIssuerProfile.IssuerURL == nil { + return fmt.Errorf("cluster must have OpenID issuer enabled to setup workload identity") + } + managedIdentityName := "humanitec-operator-identity" federatedCredentialsName := "humanitec-operator-identity" @@ -996,9 +1005,6 @@ func (p *azureProvider) enableIdentityInCluster(ctx context.Context, opCluster a } } if session.State.AzureProvider.ConfigureOperatorAccess.FederatedCredentialsName == "" { - if opCluster.Properties.OidcIssuerProfile.IssuerURL == nil { - return fmt.Errorf("cluster must have OpenID issuer enabled to setup workload identity") - } serviceAccount := fmt.Sprintf("system:serviceaccount:%s:%s", namespace, sa) resp, err := fedClient.CreateOrUpdate(ctx, resourceGroup, diff --git a/internal/cloud/common.go b/internal/cloud/common.go index e1c3745..0a9d2ac 100644 --- a/internal/cloud/common.go +++ b/internal/cloud/common.go @@ -104,7 +104,7 @@ func ensurek8sClusterRole(ctx context.Context, k8sClient *kubernetes.Clientset, }, { APIGroups: []string{"batch"}, - Resources: []string{"job"}, + Resources: []string{"jobs"}, Verbs: []string{"get", "list"}, }, { @@ -119,7 +119,7 @@ func ensurek8sClusterRole(ctx context.Context, k8sClient *kubernetes.Clientset, }, { APIGroups: []string{""}, - Resources: []string{"pods/logs"}, + Resources: []string{"pods/log"}, Verbs: []string{"get", "list"}, }, { diff --git a/internal/cloud/gcp.go b/internal/cloud/gcp.go index 1a78177..e29195e 100644 --- a/internal/cloud/gcp.go +++ b/internal/cloud/gcp.go @@ -365,6 +365,10 @@ func (p *gcpProvider) ListClusters(ctx context.Context) ([]string, error) { clusters[cluster.Name] = session.ClustersInfo{ ID: cluster.Id, Location: cluster.Location, + // When private config is not nil, then private endpoint is available + PrivateEnabled: cluster.PrivateClusterConfig != nil, + // When enable-private-endpoint is true, the public endpoint is disabled. + PrivateOnly: cluster.PrivateClusterConfig != nil && cluster.PrivateClusterConfig.EnablePrivateEndpoint, } output = append(output, cluster.Name) } @@ -577,6 +581,26 @@ func (p *gcpProvider) ConnectCluster(ctx context.Context, clusterId, loadBalance lbIp = session.State.GCPProvider.GKEClusters.LoadBalancersMap[loadBalancerName].Ip } + definitionValues := map[string]interface{}{ + "project_id": session.State.GCPProvider.GPCProject.ProjectID, + "name": clusterId, + "loadbalancer": lbIp, + "zone": clusterInfo.Location, + } + + if clusterInfo.PrivateOnly { + definitionValues["internal_ip"] = true + } else if clusterInfo.PrivateEnabled { + if b, err := message.BoolSelect("Cluster has the private endpoint enabled, do you wish to use it?"); err != nil { + return "", fmt.Errorf("failed to prompt user: %w", err) + } else if b { + definitionValues["internal_ip"] = true + // Override and set the cluster to private for the rest of the session + clusterInfo.PrivateOnly = true + session.State.GCPProvider.GKEClusters.ClustersMap[clusterId] = clusterInfo + } + } + resp, err := p.humanitecPlatform.Client.CreateResourceDefinitionWithResponse( ctx, p.humanitecPlatform.OrganizationId, client.CreateResourceDefinitionRequestRequest{ Id: humanitecClusterId, @@ -585,12 +609,7 @@ func (p *gcpProvider) ConnectCluster(ctx context.Context, clusterId, loadBalance DriverAccount: &humanitecCloudAccountId, DriverType: "humanitec/k8s-cluster-gke", DriverInputs: &client.ValuesSecretsRefsRequest{ - Values: &map[string]interface{}{ - "project_id": session.State.GCPProvider.GPCProject.ProjectID, - "name": clusterId, - "loadbalancer": lbIp, - "zone": clusterInfo.Location, - }, + Values: &definitionValues, }, }) if err != nil { @@ -606,11 +625,7 @@ func (p *gcpProvider) ConnectCluster(ctx context.Context, clusterId, loadBalance } func (p *gcpProvider) IsClusterPubliclyAvailable(ctx context.Context, clusterId string) (bool, error) { - if err := p.ensureClusterInfo(ctx); err != nil { - return false, fmt.Errorf("failed to retrieve cluster '%s' info", clusterId) - } - if p.clusterInfo.PrivateClusterConfig != nil && p.clusterInfo.PrivateClusterConfig.EnablePrivateEndpoint { - message.Info("Assuming cluster is private due to enable_private_endpoint setting") + if session.State.GCPProvider.GKEClusters.ClustersMap[clusterId].PrivateOnly { return false, nil } return true, nil diff --git a/internal/message/message.go b/internal/message/message.go index 1b84b04..931c69a 100644 --- a/internal/message/message.go +++ b/internal/message/message.go @@ -2,6 +2,7 @@ package message import ( "fmt" + "slices" "github.com/AlecAivazis/survey/v2" "github.com/aripalo/go-delightful" @@ -28,6 +29,7 @@ func SetColorMode(flag bool) { func Select(message string, options []string) (string, error) { var answer string + slices.Sort(options) prompt := survey.Select{ Message: message, Options: options, @@ -43,6 +45,7 @@ func Select(message string, options []string) (string, error) { func MultipleSelect(message string, options []string) ([]string, error) { var answers []string + slices.Sort(options) prompt := survey.MultiSelect{ Message: message, Options: options, diff --git a/internal/session/gcp.go b/internal/session/gcp.go index b38e1e5..276ace4 100644 --- a/internal/session/gcp.go +++ b/internal/session/gcp.go @@ -28,8 +28,10 @@ type GCPProviderSession struct { } type ClustersInfo struct { - ID string `json:"id"` - Location string `json:"location"` + ID string `json:"id"` + Location string `json:"location"` + PrivateEnabled bool `json:"private_enabled"` + PrivateOnly bool `json:"private_only"` } type LoadBalancerInfo struct {