Alexander Belanger 5 лет назад
Родитель
Сommit
287d8a32c7

+ 88 - 86
cli/cmd/connect/kubeconfig.go

@@ -52,104 +52,106 @@ func Kubeconfig(
 	}
 
 	for _, saCandidate := range saCandidates {
-		resolvers := make(api.CreateProjectServiceAccountRequest, 0)
-
-		for _, action := range saCandidate.Actions {
-			switch action.Name {
-			case models.ClusterCADataAction:
-				resolveAction, err := resolveClusterCAAction(action.Filename)
-
-				if err != nil {
-					return err
-				}
-
-				resolvers = append(resolvers, resolveAction)
-			case models.ClientCertDataAction:
-				resolveAction, err := resolveClientCertAction(action.Filename)
-
-				if err != nil {
-					return err
-				}
-
-				resolvers = append(resolvers, resolveAction)
-			case models.ClientKeyDataAction:
-				resolveAction, err := resolveClientKeyAction(action.Filename)
-
-				if err != nil {
-					return err
+		if len(saCandidate.Actions) > 0 {
+			resolvers := make(api.CreateProjectServiceAccountRequest, 0)
+
+			for _, action := range saCandidate.Actions {
+				switch action.Name {
+				case models.ClusterCADataAction:
+					resolveAction, err := resolveClusterCAAction(action.Filename)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.ClientCertDataAction:
+					resolveAction, err := resolveClientCertAction(action.Filename)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.ClientKeyDataAction:
+					resolveAction, err := resolveClientKeyAction(action.Filename)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.OIDCIssuerDataAction:
+					resolveAction, err := resolveOIDCIssuerAction(action.Filename)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.TokenDataAction:
+					resolveAction, err := resolveTokenDataAction(action.Filename)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.GCPKeyDataAction:
+					resolveAction, err := resolveGCPKeyAction(
+						saCandidate.ClusterEndpoint,
+						saCandidate.ClusterName,
+					)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
+				case models.AWSDataAction:
+					resolveAction, err := resolveAWSAction(
+						saCandidate.ClusterEndpoint,
+						saCandidate.ClusterName,
+						saCandidate.AWSClusterIDGuess,
+					)
+
+					if err != nil {
+						return err
+					}
+
+					resolvers = append(resolvers, resolveAction)
 				}
+			}
 
-				resolvers = append(resolvers, resolveAction)
-			case models.OIDCIssuerDataAction:
-				resolveAction, err := resolveOIDCIssuerAction(action.Filename)
+			sa, err := client.CreateProjectServiceAccount(
+				context.Background(),
+				projectID,
+				saCandidate.ID,
+				resolvers,
+			)
 
-				if err != nil {
-					return err
-				}
+			if err != nil {
+				return err
+			}
 
-				resolvers = append(resolvers, resolveAction)
-			case models.TokenDataAction:
-				resolveAction, err := resolveTokenDataAction(action.Filename)
+			for _, cluster := range sa.Clusters {
+				color.New(color.FgGreen).Printf("created service account for cluster %s with id %d\n", cluster.Name, sa.ID)
 
-				if err != nil {
-					return err
-				}
-
-				resolvers = append(resolvers, resolveAction)
-			case models.GCPKeyDataAction:
-				resolveAction, err := resolveGCPKeyAction(
-					saCandidate.ClusterEndpoint,
-					saCandidate.ClusterName,
+				// sanity check to ensure it's working
+				namespaces, err := client.GetK8sNamespaces(
+					context.Background(),
+					projectID,
+					sa.ID,
+					cluster.ID,
 				)
 
 				if err != nil {
 					return err
 				}
 
-				resolvers = append(resolvers, resolveAction)
-			case models.AWSDataAction:
-				resolveAction, err := resolveAWSAction(
-					saCandidate.ClusterEndpoint,
-					saCandidate.ClusterName,
-					saCandidate.AWSClusterIDGuess,
-				)
-
-				if err != nil {
-					return err
+				for _, ns := range namespaces.Items {
+					fmt.Println(ns.ObjectMeta.GetName())
 				}
-
-				resolvers = append(resolvers, resolveAction)
-			}
-		}
-
-		sa, err := client.CreateProjectServiceAccount(
-			context.Background(),
-			projectID,
-			saCandidate.ID,
-			resolvers,
-		)
-
-		if err != nil {
-			return err
-		}
-
-		for _, cluster := range sa.Clusters {
-			color.New(color.FgGreen).Printf("created service account for cluster %s with id %d\n", cluster.Name, sa.ID)
-
-			// sanity check to ensure it's working
-			namespaces, err := client.GetK8sNamespaces(
-				context.Background(),
-				projectID,
-				sa.ID,
-				cluster.ID,
-			)
-
-			if err != nil {
-				return err
-			}
-
-			for _, ns := range namespaces.Items {
-				fmt.Println(ns.ObjectMeta.GetName())
 			}
 		}
 	}

+ 2 - 0
internal/kubernetes/kubeconfig.go

@@ -3,6 +3,7 @@ package kubernetes
 import (
 	"context"
 	"errors"
+	"fmt"
 	"strings"
 
 	"github.com/porter-dev/porter/internal/models"
@@ -334,6 +335,7 @@ func createRawConfigFromServiceAccount(
 		authInfoMap[authInfoName].Username = sa.Username
 		authInfoMap[authInfoName].Password = sa.Password
 	case models.Bearer:
+		fmt.Println("AUTH MECHANISM IS BEARER WITH TOKEN", sa.Token)
 		authInfoMap[authInfoName].Token = sa.Token
 	case models.OIDC:
 		authInfoMap[authInfoName].AuthProvider = &api.AuthProviderConfig{

+ 23 - 17
internal/models/serviceaccount.go

@@ -29,6 +29,10 @@ type ServiceAccountCandidate struct {
 	ClusterEndpoint string `json:"cluster_endpoint"`
 	AuthMechanism   string `json:"auth_mechanism"`
 
+	// CreatedServiceAccountID is the ID of the service account that's eventually
+	// created
+	CreatedServiceAccountID uint `json:"create_sa_id"`
+
 	// The best-guess for the AWSClusterID, which is required by aws auth mechanisms
 	// See https://github.com/kubernetes-sigs/aws-iam-authenticator#what-is-a-cluster-id
 	AWSClusterIDGuess string `json:"aws_cluster_id_guess"`
@@ -43,14 +47,15 @@ type ServiceAccountCandidate struct {
 // ServiceAccountCandidateExternal represents the ServiceAccountCandidate type that is
 // sent over REST
 type ServiceAccountCandidateExternal struct {
-	ID                uint                           `json:"id"`
-	Actions           []ServiceAccountActionExternal `json:"actions"`
-	ProjectID         uint                           `json:"project_id"`
-	Kind              string                         `json:"kind"`
-	ClusterName       string                         `json:"cluster_name"`
-	ClusterEndpoint   string                         `json:"cluster_endpoint"`
-	AuthMechanism     string                         `json:"auth_mechanism"`
-	AWSClusterIDGuess string                         `json:"aws_cluster_id_guess"`
+	ID                      uint                           `json:"id"`
+	Actions                 []ServiceAccountActionExternal `json:"actions"`
+	ProjectID               uint                           `json:"project_id"`
+	Kind                    string                         `json:"kind"`
+	ClusterName             string                         `json:"cluster_name"`
+	ClusterEndpoint         string                         `json:"cluster_endpoint"`
+	AuthMechanism           string                         `json:"auth_mechanism"`
+	CreatedServiceAccountID uint                           `json:"created_sa_id"`
+	AWSClusterIDGuess       string                         `json:"aws_cluster_id_guess"`
 }
 
 // Externalize generates an external ServiceAccountCandidate to be shared over REST
@@ -62,14 +67,15 @@ func (s *ServiceAccountCandidate) Externalize() *ServiceAccountCandidateExternal
 	}
 
 	return &ServiceAccountCandidateExternal{
-		ID:                s.ID,
-		Actions:           actions,
-		ProjectID:         s.ProjectID,
-		Kind:              s.Kind,
-		ClusterName:       s.ClusterName,
-		ClusterEndpoint:   s.ClusterEndpoint,
-		AuthMechanism:     s.AuthMechanism,
-		AWSClusterIDGuess: s.AWSClusterIDGuess,
+		ID:                      s.ID,
+		Actions:                 actions,
+		ProjectID:               s.ProjectID,
+		Kind:                    s.Kind,
+		ClusterName:             s.ClusterName,
+		ClusterEndpoint:         s.ClusterEndpoint,
+		AuthMechanism:           s.AuthMechanism,
+		CreatedServiceAccountID: s.CreatedServiceAccountID,
+		AWSClusterIDGuess:       s.AWSClusterIDGuess,
 	}
 }
 
@@ -112,7 +118,7 @@ type ServiceAccount struct {
 
 	// TokenCache is a cache for bearer tokens with an expiry time
 	// Used by GCP and AWS mechanisms
-	TokenCache TokenCache `json:"gcp_token_cache"`
+	TokenCache TokenCache `json:"token_cache"`
 
 	// KeyData for a service account for GCP connectors
 	GCPKeyData []byte `json:"gcp_key_data"`

+ 21 - 0
internal/repository/gorm/serviceaccount.go

@@ -81,6 +81,27 @@ func (repo *ServiceAccountRepository) ListServiceAccountCandidatesByProjectID(
 	return saCandidates, nil
 }
 
+// UpdateServiceAccountCandidateCreatedSAID updates the CreatedServiceAccountID for
+// a candidate, after the candidate has been resolved.
+func (repo *ServiceAccountRepository) UpdateServiceAccountCandidateCreatedSAID(
+	id uint,
+	createdSAID uint,
+) (*models.ServiceAccountCandidate, error) {
+	saCandidate := &models.ServiceAccountCandidate{}
+
+	if err := repo.db.Where("id = ?", id).First(&saCandidate).Error; err != nil {
+		return nil, err
+	}
+
+	saCandidate.CreatedServiceAccountID = createdSAID
+
+	if err := repo.db.Save(saCandidate).Error; err != nil {
+		return nil, err
+	}
+
+	return saCandidate, nil
+}
+
 // CreateServiceAccount creates a new servicea account
 func (repo *ServiceAccountRepository) CreateServiceAccount(
 	sa *models.ServiceAccount,

+ 1 - 0
internal/repository/serviceaccount.go

@@ -10,6 +10,7 @@ type ServiceAccountRepository interface {
 	CreateServiceAccountCandidate(saCandidate *models.ServiceAccountCandidate) (*models.ServiceAccountCandidate, error)
 	ReadServiceAccountCandidate(id uint) (*models.ServiceAccountCandidate, error)
 	ListServiceAccountCandidatesByProjectID(projectID uint) ([]*models.ServiceAccountCandidate, error)
+	UpdateServiceAccountCandidateCreatedSAID(id uint, createdSAID uint) (*models.ServiceAccountCandidate, error)
 	CreateServiceAccount(sa *models.ServiceAccount) (*models.ServiceAccount, error)
 	ReadServiceAccount(id uint) (*models.ServiceAccount, error)
 	ListServiceAccountsByProjectID(projectID uint) ([]*models.ServiceAccount, error)

+ 16 - 0
internal/repository/test/serviceaccount.go

@@ -76,6 +76,22 @@ func (repo *ServiceAccountRepository) ListServiceAccountCandidatesByProjectID(
 	return res, nil
 }
 
+// UpdateServiceAccountCandidateCreatedSAID updates the CreatedServiceAccountID for
+// a candidate, after the candidate has been resolved.
+func (repo *ServiceAccountRepository) UpdateServiceAccountCandidateCreatedSAID(
+	id uint,
+	createdSAID uint,
+) (*models.ServiceAccountCandidate, error) {
+	if !repo.canQuery {
+		return nil, errors.New("Cannot write database")
+	}
+
+	index := int(id - 1)
+	repo.serviceAccountCandidates[index].CreatedServiceAccountID = createdSAID
+
+	return repo.serviceAccountCandidates[index], nil
+}
+
 // CreateServiceAccount creates a new servicea account
 func (repo *ServiceAccountRepository) CreateServiceAccount(
 	sa *models.ServiceAccount,

+ 22 - 0
server/api/project_handler.go

@@ -207,6 +207,14 @@ func (app *App) HandleCreateProjectSACandidates(w http.ResponseWriter, r *http.R
 		// if the SA candidate does not have any actions to perform, create the ServiceAccount
 		// automatically
 		if len(saCandidate.Actions) == 0 {
+			// we query the repo again to get the decrypted version of the SA candidate
+			saCandidate, err = app.repo.ServiceAccount.ReadServiceAccountCandidate(saCandidate.ID)
+
+			if err != nil {
+				app.handleErrorDataRead(err, w)
+				return
+			}
+
 			saForm := &forms.ServiceAccountActionResolver{
 				ServiceAccountCandidateID: saCandidate.ID,
 				SACandidate:               saCandidate,
@@ -226,6 +234,13 @@ func (app *App) HandleCreateProjectSACandidates(w http.ResponseWriter, r *http.R
 				return
 			}
 
+			saCandidate, err = app.repo.ServiceAccount.UpdateServiceAccountCandidateCreatedSAID(saCandidate.ID, sa.ID)
+
+			if err != nil {
+				app.handleErrorDataWrite(err, w)
+				return
+			}
+
 			app.logger.Info().Msgf("New service account created: %d", sa.ID)
 		}
 
@@ -377,6 +392,13 @@ func (app *App) HandleResolveSACandidateActions(w http.ResponseWriter, r *http.R
 	if sa != nil {
 		app.logger.Info().Msgf("New service account created: %d", sa.ID)
 
+		_, err := app.repo.ServiceAccount.UpdateServiceAccountCandidateCreatedSAID(uint(candID), sa.ID)
+
+		if err != nil {
+			app.handleErrorDataWrite(err, w)
+			return
+		}
+
 		saExternal := sa.Externalize()
 
 		w.WriteHeader(http.StatusCreated)

+ 1 - 1
server/api/project_handler_test.go

@@ -152,7 +152,7 @@ var createProjectSACandidatesTests = []*projTest{
 		endpoint:  "/api/projects/1/candidates",
 		body:      `{"kubeconfig":"` + OIDCAuthWithDataForJSON + `"}`,
 		expStatus: http.StatusCreated,
-		expBody:   `[{"id":1,"actions":[],"project_id":1,"kind":"connector","cluster_name":"cluster-test","cluster_endpoint":"https://localhost","auth_mechanism":"oidc"}]`,
+		expBody:   `[{"id":1,"actions":[],"created_sa_id":1,"project_id":1,"kind":"connector","cluster_name":"cluster-test","cluster_endpoint":"https://localhost","auth_mechanism":"oidc"}]`,
 		useCookie: true,
 		validators: []func(c *projTest, tester *tester, t *testing.T){
 			projectSACandidateBodyValidator,