Просмотр исходного кода

Merge branches 'nico/credentials-preview-aws' and 'nico/new-onboarding-flow' of github.com:porter-dev/porter into nico/new-onboarding-flow

jnfrati 4 лет назад
Родитель
Сommit
983cd81b83

+ 2 - 0
api/server/handlers/oauth_callback/digitalocean.go

@@ -71,6 +71,8 @@ func (p *OAuthCallbackDOHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		ProjectID: projID,
 	}
 
+	oauthInt.PopulateTargetMetadata()
+
 	// create the oauth integration first
 	oauthInt, err = p.Repo().OAuthIntegration().CreateOAuthIntegration(oauthInt)
 

+ 11 - 0
api/server/handlers/project/create.go

@@ -51,6 +51,17 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	// create onboarding flow set to the first step
+	_, err = p.Repo().Onboarding().CreateProjectOnboarding(&models.Onboarding{
+		ProjectID:   proj.ID,
+		CurrentStep: types.StepConnectSource,
+	})
+
+	if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
 	// create default project usage restriction
 	_, err = p.Repo().ProjectUsage().CreateProjectUsage(&models.ProjectUsage{
 		ProjectID:      proj.ID,

+ 4 - 0
api/server/handlers/provision/provision_gcr.go

@@ -11,6 +11,7 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
+	"github.com/porter-dev/porter/internal/kubernetes/provisioner/gcp/gcr"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
 	"gorm.io/gorm"
@@ -107,6 +108,9 @@ func (c *ProvisionGCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		}
 	}
 
+	opts.GCR = &gcr.Conf{
+		GCPProjectID: gcpInt.GCPProjectID,
+	}
 	opts.CredentialExchange.VaultToken = vaultToken
 	opts.OperationKind = provisioner.Apply
 

+ 4 - 2
api/server/handlers/provision/provision_gke.go

@@ -110,8 +110,10 @@ func (c *ProvisionGKEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 
 	opts.CredentialExchange.VaultToken = vaultToken
 	opts.GKE = &gke.Conf{
-		ClusterName: request.GKEName,
-		IssuerEmail: request.IssuerEmail,
+		GCPProjectID: gcpInt.GCPProjectID,
+		GCPRegion:    request.GCPRegion,
+		ClusterName:  request.GKEName,
+		IssuerEmail:  request.IssuerEmail,
 	}
 
 	opts.OperationKind = provisioner.Apply

+ 2 - 2
api/types/project.go

@@ -73,8 +73,8 @@ type GetProjectBillingResponse struct {
 type StepEnum string
 
 const (
-	StepGithub StepEnum = "github"
-	StepTwo    StepEnum = "step_two"
+	StepConnectSource StepEnum = "connect_source"
+	StepGithub        StepEnum = "github"
 )
 
 type ConnectedSourceType string

+ 8 - 0
api/types/project_integration.go

@@ -26,6 +26,14 @@ type OAuthIntegration struct {
 
 	// The project that this integration belongs to
 	ProjectID uint `json:"project_id"`
+
+	// (optional) an identifying email on the target identity provider.
+	// for example, for DigitalOcean this is the user's email.
+	TargetEmail string `json:"target_email,omitempty"`
+
+	// (optional) an identifying string on the target identity provider.
+	// for example, for DigitalOcean this is the target project name.
+	TargetName string `json:"target_id,omitempty"`
 }
 
 type ListOAuthResponse []*OAuthIntegration

+ 1 - 0
api/types/provision.go

@@ -21,6 +21,7 @@ type CreateGCRInfraRequest struct {
 
 type CreateGKEInfraRequest struct {
 	GKEName          string `json:"gke_name" form:"required"`
+	GCPRegion        string `json:"gcp_region" form:"required"`
 	IssuerEmail      string `json:"issuer_email" form:"required"`
 	ProjectID        uint   `json:"-" form:"required"`
 	GCPIntegrationID uint   `json:"gcp_integration_id" form:"required"`

+ 0 - 3
dashboard/src/main/home/integrations/create-integration/GCRForm.tsx

@@ -16,7 +16,6 @@ type PropsType = {
 
 type StateType = {
   credentialsName: string;
-  gcpRegion: string;
   serviceAccountKey: string;
   gcpProjectID: string;
   url: string;
@@ -25,7 +24,6 @@ type StateType = {
 export default class GCRForm extends Component<PropsType, StateType> {
   state = {
     credentialsName: "",
-    gcpRegion: "",
     serviceAccountKey: "",
     gcpProjectID: "",
     url: "",
@@ -48,7 +46,6 @@ export default class GCRForm extends Component<PropsType, StateType> {
       .createGCPIntegration(
         "<token>",
         {
-          gcp_region: this.state.gcpRegion,
           gcp_key_data: this.state.serviceAccountKey,
           gcp_project_id: this.state.gcpProjectID,
         },

+ 1 - 18
dashboard/src/main/home/integrations/edit-integration/GCRForm.tsx

@@ -16,7 +16,6 @@ type PropsType = {
 
 type StateType = {
   credentialsName: string;
-  gcpRegion: string;
   serviceAccountKey: string;
   gcpProjectID: string;
   url: string;
@@ -25,22 +24,15 @@ type StateType = {
 export default class GCRForm extends Component<PropsType, StateType> {
   state = {
     credentialsName: "",
-    gcpRegion: "",
     serviceAccountKey: "",
     gcpProjectID: "",
     url: "",
   };
 
   isDisabled = (): boolean => {
-    let {
-      credentialsName,
-      gcpRegion,
-      gcpProjectID,
-      serviceAccountKey,
-    } = this.state;
+    let { credentialsName, gcpProjectID, serviceAccountKey } = this.state;
     if (
       credentialsName === "" ||
-      gcpRegion === "" ||
       serviceAccountKey === "" ||
       gcpProjectID === ""
     ) {
@@ -58,7 +50,6 @@ export default class GCRForm extends Component<PropsType, StateType> {
       .createGCPIntegration(
         "<token>",
         {
-          gcp_region: this.state.gcpRegion,
           gcp_key_data: this.state.serviceAccountKey,
           gcp_project_id: this.state.gcpProjectID,
         },
@@ -106,14 +97,6 @@ export default class GCRForm extends Component<PropsType, StateType> {
           />
           <Heading>GCP Settings</Heading>
           <Helper>Service account credentials for GCP permissions.</Helper>
-          <InputRow
-            type="text"
-            value={this.state.gcpRegion}
-            setValue={(gcpRegion: string) => this.setState({ gcpRegion })}
-            label="📍 GCP Region"
-            placeholder="ex: uranus-north3"
-            width="100%"
-          />
           <TextArea
             value={this.state.serviceAccountKey}
             setValue={(serviceAccountKey: string) =>

+ 0 - 2
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_GCPRegistryForm.tsx

@@ -46,7 +46,6 @@ export const CredentialsForm: React.FC<{
         .createGCPIntegration(
           "<token>",
           {
-            gcp_region: "",
             gcp_key_data: serviceAccountKey,
             gcp_project_id: projectId,
           },
@@ -213,7 +212,6 @@ export const TestRegistryConnection: React.FC<{
   return (
     <>
       <RegistryImageList
-        registryType="gcr"
         project={snap.project}
         registry_id={snap.connected_registry.settings.registry_connection_id}
       />

+ 0 - 1
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_AWSProvisionerForm.tsx

@@ -315,7 +315,6 @@ export const SettingsForm: React.FC<{
           {
             aws_integration_id: awsIntegrationId,
             ecr_name: `${project.name}-registry`,
-            issuer_email: snap.StateHandler.user_email,
           },
           { id: project.id }
         )

+ 0 - 1
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_DOProvisionerForm.tsx

@@ -190,7 +190,6 @@ export const SettingsForm: React.FC<{
             do_integration_id: integrationId,
             docr_name: project.name,
             docr_subscription_tier: tier,
-            issuer_email: snap.StateHandler.user_email,
           },
           {
             project_id: project.id,

+ 13 - 15
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_GCPProvisionerForm.tsx

@@ -47,7 +47,6 @@ export const CredentialsForm: React.FC<{
 }> = ({ nextFormStep, project }) => {
   const [projectId, setProjectId] = useState("");
   const [serviceAccountKey, setServiceAccountKey] = useState("");
-  const [region, setRegion] = useState("us-east1");
   const [buttonStatus, setButtonStatus] = useState("");
 
   const validate = () => {
@@ -77,7 +76,6 @@ export const CredentialsForm: React.FC<{
         .createGCPIntegration(
           "<token>",
           {
-            gcp_region: region,
             gcp_key_data: serviceAccountKey,
             gcp_project_id: projectId,
           },
@@ -117,18 +115,6 @@ export const CredentialsForm: React.FC<{
         height="100%"
         isRequired={true}
       />
-      <SelectRow
-        options={regionOptions}
-        width="100%"
-        value={region}
-        scrollBuffer={true}
-        dropdownMaxHeight="240px"
-        setActiveValue={(x: string) => {
-          setRegion(x);
-        }}
-        label="📍 GCP Region"
-      />
-      <Br />
       <SaveButton
         text="Continue"
         disabled={false}
@@ -146,6 +132,7 @@ export const SettingsForm: React.FC<{
   nextFormStep: (data: Partial<GCPProvisionerConfig>) => void;
   project: any;
 }> = ({ nextFormStep, project }) => {
+  const [region, setRegion] = useState("us-east1");
   const [clusterName, setClusterName] = useState(`${project.name}-cluster`);
   const [buttonStatus, setButtonStatus] = useState("");
   const snap = useSnapshot(OFState);
@@ -232,7 +219,6 @@ export const SettingsForm: React.FC<{
         "<token>",
         {
           gcp_integration_id: id,
-          issuer_email: snap.StateHandler.user_email,
         },
         { project_id: project.id }
       );
@@ -249,6 +235,7 @@ export const SettingsForm: React.FC<{
       const res = await api.createGKE(
         "<token>",
         {
+          gcp_region: region,
           gke_name: clusterName,
           gcp_integration_id: id,
           issuer_email: snap.StateHandler.user_email,
@@ -274,6 +261,17 @@ export const SettingsForm: React.FC<{
         width="100%"
         isRequired={true}
       />
+      <SelectRow
+        options={regionOptions}
+        width="100%"
+        value={region}
+        scrollBuffer={true}
+        dropdownMaxHeight="240px"
+        setActiveValue={(x: string) => {
+          setRegion(x);
+        }}
+        label="📍 GCP Region"
+      />
       <Br />
       <SaveButton
         text="Provision resources"

+ 2 - 1
dashboard/src/main/home/provisioner/GCPFormSection.tsx

@@ -173,8 +173,10 @@ const GCPFormSectionFC: React.FC<PropsType> = (props) => {
       .createGKE(
         "<token>",
         {
+          gcp_region: gcpRegion,
           gke_name: clusterName,
           gcp_integration_id: id,
+          issuer_email: context.user.email,
         },
         { project_id: currentProject.id }
       )
@@ -192,7 +194,6 @@ const GCPFormSectionFC: React.FC<PropsType> = (props) => {
       .createGCPIntegration(
         "<token>",
         {
-          gcp_region: gcpRegion,
           gcp_key_data: gcpKeyData,
           gcp_project_id: gcpProjectId,
         },

+ 1 - 4
dashboard/src/shared/api.tsx

@@ -80,7 +80,6 @@ const createDOCR = baseApi<
     do_integration_id: number;
     docr_name: string;
     docr_subscription_tier: string;
-    issuer_email: string;
   },
   {
     project_id: number;
@@ -109,7 +108,6 @@ const createEmailVerification = baseApi<{}, {}>("POST", (pathParams) => {
 
 const createGCPIntegration = baseApi<
   {
-    gcp_region: string;
     gcp_key_data: string;
     gcp_project_id: string;
   },
@@ -123,7 +121,6 @@ const createGCPIntegration = baseApi<
 const createGCR = baseApi<
   {
     gcp_integration_id: number;
-    issuer_email: string;
   },
   {
     project_id: number;
@@ -134,6 +131,7 @@ const createGCR = baseApi<
 
 const createGKE = baseApi<
   {
+    gcp_region: string;
     gcp_integration_id: number;
     gke_name: string;
     issuer_email: string;
@@ -836,7 +834,6 @@ const provisionECR = baseApi<
   {
     ecr_name: string;
     aws_integration_id: number;
-    issuer_email: string;
   },
   { id: number }
 >("POST", (pathParams) => {

+ 0 - 30
internal/kubernetes/provisioner/aws/aws.go

@@ -1,30 +0,0 @@
-package aws
-
-import (
-	v1 "k8s.io/api/core/v1"
-)
-
-// Conf wraps the AWS integration model
-type Conf struct {
-	AWSRegion, AWSAccessKeyID, AWSSecretAccessKey string
-}
-
-// AttachAWSEnv adds the relevant AWS env for the provisioner
-func (conf *Conf) AttachAWSEnv(env []v1.EnvVar) []v1.EnvVar {
-	env = append(env, v1.EnvVar{
-		Name:  "AWS_REGION",
-		Value: conf.AWSRegion,
-	})
-
-	env = append(env, v1.EnvVar{
-		Name:  "AWS_ACCESS_KEY_ID",
-		Value: conf.AWSAccessKeyID,
-	})
-
-	env = append(env, v1.EnvVar{
-		Name:  "AWS_SECRET_ACCESS_KEY",
-		Value: conf.AWSSecretAccessKey,
-	})
-
-	return env
-}

+ 18 - 0
internal/kubernetes/provisioner/gcp/gcr/gcr.go

@@ -0,0 +1,18 @@
+package gcr
+
+import v1 "k8s.io/api/core/v1"
+
+// Conf is the GCR cluster config required for the provisioner
+type Conf struct {
+	GCPProjectID string
+}
+
+// AttachGCREnv adds the relevant GCR env for the provisioner
+func (conf *Conf) AttachGCREnv(env []v1.EnvVar) []v1.EnvVar {
+	env = append(env, v1.EnvVar{
+		Name:  "GCP_PROJECT_ID",
+		Value: conf.GCPProjectID,
+	})
+
+	return env
+}

+ 11 - 1
internal/kubernetes/provisioner/gcp/gke/gke.go

@@ -4,11 +4,21 @@ import v1 "k8s.io/api/core/v1"
 
 // Conf is the GKE cluster config required for the provisioner
 type Conf struct {
-	ClusterName, IssuerEmail string
+	GCPRegion, GCPProjectID, ClusterName, IssuerEmail string
 }
 
 // AttachGKEEnv adds the relevant GKE env for the provisioner
 func (conf *Conf) AttachGKEEnv(env []v1.EnvVar) []v1.EnvVar {
+	env = append(env, v1.EnvVar{
+		Name:  "GCP_REGION",
+		Value: conf.GCPRegion,
+	})
+
+	env = append(env, v1.EnvVar{
+		Name:  "GCP_PROJECT_ID",
+		Value: conf.GCPProjectID,
+	})
+
 	env = append(env, v1.EnvVar{
 		Name:  "GKE_CLUSTER_NAME",
 		Value: conf.ClusterName,

+ 4 - 0
internal/kubernetes/provisioner/provisioner.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner/aws/eks"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner/do/docr"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner/do/doks"
+	"github.com/porter-dev/porter/internal/kubernetes/provisioner/gcp/gcr"
 	"github.com/porter-dev/porter/internal/kubernetes/provisioner/gcp/gke"
 	"github.com/porter-dev/porter/internal/models"
 	batchv1 "k8s.io/api/batch/v1"
@@ -43,6 +44,7 @@ type ProvisionOpts struct {
 	// resource-specific opts
 	ECR  *ecr.Conf
 	EKS  *eks.Conf
+	GCR  *gcr.Conf
 	GKE  *gke.Conf
 	DOCR *docr.Conf
 	DOKS *doks.Conf
@@ -73,6 +75,8 @@ func GetProvisionerJobTemplate(opts *ProvisionOpts) (*batchv1.Job, error) {
 		env = opts.ECR.AttachECREnv(env)
 	case types.InfraEKS:
 		env = opts.EKS.AttachEKSEnv(env)
+	case types.InfraGCR:
+		env = opts.GCR.AttachGCREnv(env)
 	case types.InfraGKE:
 		env = opts.GKE.AttachGKEEnv(env)
 	case types.InfraDOCR:

+ 36 - 5
internal/models/integrations/oauth.go

@@ -1,8 +1,10 @@
 package integrations
 
 import (
+	"context"
 	"time"
 
+	"github.com/digitalocean/godo"
 	"github.com/porter-dev/porter/api/types"
 	"gorm.io/gorm"
 )
@@ -38,11 +40,38 @@ type OAuthIntegration struct {
 	// The project that this integration belongs to
 	ProjectID uint `json:"project_id"`
 
+	// (optional) an identifying email on the target identity provider.
+	// for example, for DigitalOcean this is the user's email.
+	TargetEmail string `json:"target_email"`
+
+	// (optional) an identifying string on the target identity provider.
+	// for example, for DigitalOcean this is the target project name.
+	TargetName string `json:"target_id"`
+
 	// ------------------------------------------------------------------
 	// All fields encrypted before storage.
 	// ------------------------------------------------------------------
 }
 
+func (g *OAuthIntegration) PopulateTargetMetadata() {
+	switch g.Client {
+	case types.OAuthDigitalOcean:
+		client := godo.NewFromToken(string(g.AccessToken))
+
+		account, _, err := client.Account.Get(context.TODO())
+
+		if err == nil && account != nil {
+			g.TargetEmail = account.Email
+		}
+
+		proj, _, err := client.Projects.GetDefault(context.TODO())
+
+		if err == nil && proj != nil {
+			g.TargetName = proj.Name
+		}
+	}
+}
+
 // GithubAppOAuthIntegration is the model used for storing github app oauth data
 // Unlike the above, this model is tied to a specific user, not a project
 type GithubAppOAuthIntegration struct {
@@ -56,10 +85,12 @@ type GithubAppOAuthIntegration struct {
 // ToOAuthIntegrationType generates an external OAuthIntegration to be shared over REST
 func (o *OAuthIntegration) ToOAuthIntegrationType() *types.OAuthIntegration {
 	return &types.OAuthIntegration{
-		CreatedAt: o.CreatedAt,
-		ID:        o.ID,
-		Client:    o.Client,
-		UserID:    o.UserID,
-		ProjectID: o.ProjectID,
+		CreatedAt:   o.CreatedAt,
+		ID:          o.ID,
+		Client:      o.Client,
+		UserID:      o.UserID,
+		ProjectID:   o.ProjectID,
+		TargetEmail: o.TargetEmail,
+		TargetName:  o.TargetName,
 	}
 }

+ 2 - 6
internal/repository/test/repository.go

@@ -139,13 +139,12 @@ func (t *TestRepository) ProjectUsage() repository.ProjectUsageRepository {
 	return t.projectUsage
 }
 
-<<<<<<< HEAD
 func (t *TestRepository) Onboarding() repository.ProjectOnboardingRepository {
 	return t.onboarding
-=======
+}
+
 func (t *TestRepository) CredentialsExchangeToken() repository.CredentialsExchangeTokenRepository {
 	return t.ceToken
->>>>>>> belanger/por-132-vault-storage-backend
 }
 
 // NewRepository returns a Repository which persists users in memory
@@ -178,10 +177,7 @@ func NewRepository(canQuery bool, failingMethods ...string) repository.Repositor
 		notificationConfig:        NewNotificationConfigRepository(canQuery),
 		event:                     NewEventRepository(canQuery),
 		projectUsage:              NewProjectUsageRepository(canQuery),
-<<<<<<< HEAD
 		onboarding:                NewProjectOnboardingRepository(canQuery),
-=======
 		ceToken:                   NewCredentialsExchangeTokenRepository(canQuery),
->>>>>>> belanger/por-132-vault-storage-backend
 	}
 }