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

Merge branch 'nico/new-onboarding-flow' of https://github.com/porter-dev/porter into nico/new-onboarding-flow

jusrhee 4 лет назад
Родитель
Сommit
74419529b4
28 измененных файлов с 176 добавлено и 109 удалено
  1. 14 2
      api/server/handlers/infra/delete.go
  2. 2 0
      api/server/handlers/oauth_callback/digitalocean.go
  3. 11 0
      api/server/handlers/project/create.go
  4. 4 0
      api/server/handlers/provision/provision_gcr.go
  5. 4 2
      api/server/handlers/provision/provision_gke.go
  6. 2 2
      api/types/project.go
  7. 8 0
      api/types/project_integration.go
  8. 1 0
      api/types/provision.go
  9. 1 1
      dashboard/src/components/ProvisionerStatus.tsx
  10. 0 3
      dashboard/src/main/home/integrations/create-integration/GCRForm.tsx
  11. 1 18
      dashboard/src/main/home/integrations/edit-integration/GCRForm.tsx
  12. 1 2
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_DORegistryForm.tsx
  13. 0 2
      dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_GCPRegistryForm.tsx
  14. 3 1
      dashboard/src/main/home/onboarding/steps/ConnectSource.tsx
  15. 23 4
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/SharedStatus.tsx
  16. 0 1
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_AWSProvisionerForm.tsx
  17. 0 2
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_DOProvisionerForm.tsx
  18. 13 15
      dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_GCPProvisionerForm.tsx
  19. 2 3
      dashboard/src/main/home/provisioner/GCPFormSection.tsx
  20. 13 4
      dashboard/src/shared/api.tsx
  21. 0 30
      internal/kubernetes/provisioner/aws/aws.go
  22. 18 0
      internal/kubernetes/provisioner/gcp/gcr/gcr.go
  23. 11 1
      internal/kubernetes/provisioner/gcp/gke/gke.go
  24. 0 4
      internal/kubernetes/provisioner/global_stream.go
  25. 5 1
      internal/kubernetes/provisioner/provisioner.go
  26. 1 0
      internal/models/integrations/gcp.go
  27. 36 5
      internal/models/integrations/oauth.go
  28. 2 6
      internal/repository/test/repository.go

+ 14 - 2
api/server/handlers/infra/delete.go

@@ -107,9 +107,12 @@ func destroyECR(conf *config.Config, infra *models.Infra) error {
 	}
 
 	opts.CredentialExchange.VaultToken = vaultToken
+
 	opts.ECR = &ecr.Conf{
-		ECRName: lastAppliedECR.ECRName,
+		AWSRegion: awsInt.AWSRegion,
+		ECRName:   lastAppliedECR.ECRName,
 	}
+
 	opts.OperationKind = provisioner.Destroy
 
 	err = conf.ProvisionerAgent.Provision(opts)
@@ -144,9 +147,12 @@ func destroyEKS(conf *config.Config, infra *models.Infra) error {
 	}
 
 	opts.CredentialExchange.VaultToken = vaultToken
+
 	opts.EKS = &eks.Conf{
+		AWSRegion:   awsInt.AWSRegion,
 		ClusterName: lastAppliedEKS.EKSName,
 		MachineType: lastAppliedEKS.MachineType,
+		IssuerEmail: lastAppliedEKS.IssuerEmail,
 	}
 	opts.OperationKind = provisioner.Destroy
 
@@ -182,6 +188,7 @@ func destroyDOCR(conf *config.Config, infra *models.Infra) error {
 	}
 
 	opts.CredentialExchange.VaultToken = vaultToken
+
 	opts.DOCR = &docr.Conf{
 		DOCRName:             lastAppliedDOCR.DOCRName,
 		DOCRSubscriptionTier: lastAppliedDOCR.DOCRSubscriptionTier,
@@ -221,9 +228,11 @@ func destroyDOKS(conf *config.Config, infra *models.Infra) error {
 	}
 
 	opts.CredentialExchange.VaultToken = vaultToken
+
 	opts.DOKS = &doks.Conf{
 		DORegion:        lastAppliedDOKS.DORegion,
 		DOKSClusterName: lastAppliedDOKS.DOKSName,
+		IssuerEmail:     lastAppliedDOKS.IssuerEmail,
 	}
 
 	opts.OperationKind = provisioner.Destroy
@@ -261,7 +270,10 @@ func destroyGKE(conf *config.Config, infra *models.Infra) error {
 
 	opts.CredentialExchange.VaultToken = vaultToken
 	opts.GKE = &gke.Conf{
-		ClusterName: lastAppliedGKE.GKEName,
+		GCPProjectID: gcpInt.GCPProjectID,
+		GCPRegion:    lastAppliedGKE.GCPRegion,
+		ClusterName:  lastAppliedGKE.GKEName,
+		IssuerEmail:  lastAppliedGKE.IssuerEmail,
 	}
 
 	opts.OperationKind = provisioner.Destroy

+ 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"`

+ 1 - 1
dashboard/src/components/ProvisionerStatus.tsx

@@ -16,6 +16,7 @@ export interface TFModule {
   status: string;
   created_at: string;
   global_errors?: TFResourceError[];
+  got_desired: boolean;
   // optional resources, if not created
   resources?: TFResource[];
 }
@@ -75,7 +76,6 @@ const ProvisionerStatus: React.FC<Props> = ({ modules }) => {
 
   const renderModules = () => {
     return modules.map((val) => {
-      console.log(val);
       const totalResources = val.resources?.length;
       const provisionedResources = val.resources?.filter((resource) => {
         return resource.provisioned;

+ 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) =>

+ 1 - 2
dashboard/src/main/home/onboarding/steps/ConnectRegistry/forms/_DORegistryForm.tsx

@@ -49,7 +49,7 @@ export const CredentialsForm: React.FC<{
         // Sort decendant
         integrations.sort((a, b) => b.id - a.id);
         let lastUsed = integrations.find((i) => {
-          i.id === snap.StateHandler?.provision_resources?.credentials?.id;
+          i.id === snap.StateHandler?.connected_registry?.credentials?.id;
         });
         if (!lastUsed) {
           lastUsed = integrations[0];
@@ -85,7 +85,6 @@ export const CredentialsForm: React.FC<{
         </div>
       )}
       <ConnectDigitalOceanButton
-        target={"_blank"}
         href={`/api/projects/${project?.id}/oauth/digitalocean?redirect_uri=${encoded_redirect_uri}`}
       >
         {connectedAccount !== null

+ 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}
       />

+ 3 - 1
dashboard/src/main/home/onboarding/steps/ConnectSource.tsx

@@ -110,7 +110,9 @@ const ConnectSource: React.FC<{
           </List>
           <Helper>
             Don't see the right repos?{" "}
-            <A href={"/api/integrations/github-app/install"}>
+            <A
+              href={`/api/integrations/github-app/install?redirect_uri=${encoded_redirect_uri}`}
+            >
               Install Porter in more repositories
             </A>
             .

+ 23 - 4
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/SharedStatus.tsx

@@ -25,7 +25,8 @@ export const SharedStatus: React.FC<{
     index: number,
     addedResources: TFResource[],
     erroredResources: TFResource[],
-    globalErrors: TFResourceError[]
+    globalErrors: TFResourceError[],
+    gotDesired?: boolean
   ) => {
     if (!tfModules[index]?.resources) {
       tfModules[index].resources = [];
@@ -35,6 +36,10 @@ export const SharedStatus: React.FC<{
       tfModules[index].global_errors = [];
     }
 
+    if (gotDesired) {
+      tfModules[index].got_desired = true;
+    }
+
     let resources = tfModules[index].resources;
 
     // construct map of tf resources addresses to indices
@@ -53,6 +58,12 @@ export const SharedStatus: React.FC<{
       } else {
         resources.push(addedResource);
         resourceAddrMap.set(addedResource.addr, resources.length - 1);
+
+        // if the resource is being added but there's not a desired state, re-query for the
+        // desired state
+        if (!tfModules[index].got_desired) {
+          updateDesiredState(index, tfModules[index]);
+        }
       }
     }
 
@@ -123,12 +134,17 @@ export const SharedStatus: React.FC<{
                 ? 1
                 : 0;
 
+            // if there's a global error, or the number of resources that errored_out is
+            // greater than 0, this resource is in an error state
             numModulesErrored +=
+              tfModule.global_errors?.length > 0 ||
               tfModule.resources.filter(
                 (resource) => resource.errored?.errored_out
               ).length > 0
                 ? 1
                 : 0;
+          } else if (tfModule.global_errors?.length > 0) {
+            numModulesErrored += 1;
           }
         }
       }
@@ -144,6 +160,8 @@ export const SharedStatus: React.FC<{
           hasError: true,
         });
       }
+    } else {
+      setInfraStatus(null);
     }
   }, [tfModules]);
 
@@ -240,7 +258,7 @@ export const SharedStatus: React.FC<{
       };
     });
 
-    updateTFModules(index, addedResources, [], []);
+    updateTFModules(index, addedResources, [], [], true);
   };
 
   const updateDesiredState = (index: number, val: TFModule) => {
@@ -290,8 +308,8 @@ export const SharedStatus: React.FC<{
         if (filter.length == 0) {
           matchedInfras.set(infra.kind + "-" + infra.id, infra);
         } else if (
-          (filter.includes(infra.kind) && matchedInfras.get(infra.Kind)?.id) ||
-          0 < infra.id
+          filter.includes(infra.kind) &&
+          (matchedInfras.get(infra.Kind)?.id || 0 < infra.id)
         ) {
           matchedInfras.set(infra.kind, infra);
         }
@@ -303,6 +321,7 @@ export const SharedStatus: React.FC<{
           id: infra.id,
           kind: infra.kind,
           status: infra.status,
+          got_desired: false,
           created_at: infra.created_at,
         };
 

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

@@ -203,7 +203,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 - 2
dashboard/src/main/home/onboarding/steps/ProvisionResources/forms/_DOProvisionerForm.tsx

@@ -107,7 +107,6 @@ export const CredentialsForm: React.FC<{
         </div>
       )}
       <ConnectDigitalOceanButton
-        target={"_blank"}
         href={`/api/projects/${project?.id}/oauth/digitalocean?redirect_uri=${encoded_redirect_uri}`}
       >
         {connectedAccount !== null
@@ -190,7 +189,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 - 3
dashboard/src/main/home/provisioner/GCPFormSection.tsx

@@ -151,7 +151,6 @@ const GCPFormSectionFC: React.FC<PropsType> = (props) => {
   };
 
   const provisionGCR = (id: number) => {
-    console.log("Provisioning GCR");
     let { currentProject } = context;
 
     return api
@@ -166,15 +165,16 @@ const GCPFormSectionFC: React.FC<PropsType> = (props) => {
   };
 
   const provisionGKE = (id: number) => {
-    console.log("Provisioning GKE");
     let { currentProject } = context;
 
     api
       .createGKE(
         "<token>",
         {
+          gcp_region: gcpRegion,
           gke_name: clusterName,
           gcp_integration_id: id,
+          issuer_email: context.user.email,
         },
         { project_id: currentProject.id }
       )
@@ -192,7 +192,6 @@ const GCPFormSectionFC: React.FC<PropsType> = (props) => {
       .createGCPIntegration(
         "<token>",
         {
-          gcp_region: gcpRegion,
           gcp_key_data: gcpKeyData,
           gcp_project_id: gcpProjectId,
         },

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

@@ -44,6 +44,16 @@ const connectDORegistry = baseApi<
   return `/api/projects/${pathParams.project_id}/registries`;
 });
 
+const getAWSIntegration = baseApi<{}, { project_id: number }>(
+  "GET",
+  ({ project_id }) => `/api/projects/${project_id}/integrations/aws`
+);
+
+const getGCPIntegration = baseApi<{}, { project_id: number }>(
+  "GET",
+  ({ project_id }) => `/api/projects/${project_id}/integrations/gcp`
+);
+
 const createAWSIntegration = baseApi<
   {
     aws_region: string;
@@ -75,7 +85,6 @@ const createDOCR = baseApi<
     do_integration_id: number;
     docr_name: string;
     docr_subscription_tier: string;
-    issuer_email: string;
   },
   {
     project_id: number;
@@ -104,7 +113,6 @@ const createEmailVerification = baseApi<{}, {}>("POST", (pathParams) => {
 
 const createGCPIntegration = baseApi<
   {
-    gcp_region: string;
     gcp_key_data: string;
     gcp_project_id: string;
   },
@@ -118,7 +126,6 @@ const createGCPIntegration = baseApi<
 const createGCR = baseApi<
   {
     gcp_integration_id: number;
-    issuer_email: string;
   },
   {
     project_id: number;
@@ -129,6 +136,7 @@ const createGCR = baseApi<
 
 const createGKE = baseApi<
   {
+    gcp_region: string;
     gcp_integration_id: number;
     gke_name: string;
     issuer_email: string;
@@ -831,7 +839,6 @@ const provisionECR = baseApi<
   {
     ecr_name: string;
     aws_integration_id: number;
-    issuer_email: string;
   },
   { id: number }
 >("POST", (pathParams) => {
@@ -1131,6 +1138,8 @@ export default {
   connectECRRegistry,
   connectGCRRegistry,
   connectDORegistry,
+  getAWSIntegration,
+  getGCPIntegration,
   createAWSIntegration,
   overwriteAWSIntegration,
   createDOCR,

+ 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,

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

@@ -106,13 +106,9 @@ func GlobalStreamListener(
 
 		// parse messages from the global stream
 		for _, msg := range xstreams[0].Messages {
-			fmt.Println("GOT REDIS GLOBAL MSG", msg, msg.Values["id"], msg.Values["status"])
-
 			// parse the id to identify the infra
 			kind, projID, infraID, err := models.ParseUniqueName(fmt.Sprintf("%v", msg.Values["id"]))
 
-			fmt.Println("PARSED DATA IS", kind, projID, infraID, err)
-
 			if fmt.Sprintf("%v", msg.Values["status"]) == "created" {
 				infra, err := repo.Infra().ReadInfra(projID, infraID)
 

+ 5 - 1
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:
@@ -83,7 +87,7 @@ func GetProvisionerJobTemplate(opts *ProvisionOpts) (*batchv1.Job, error) {
 
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      opts.Infra.GetUniqueName(),
+			Name:      fmt.Sprintf("%s-%s", string(opts.OperationKind), opts.Infra.GetUniqueName()),
 			Namespace: opts.ProvJobNamespace,
 			Labels:    labels,
 		},

+ 1 - 0
internal/models/integrations/gcp.go

@@ -44,6 +44,7 @@ type GCPIntegration struct {
 
 func (g *GCPIntegration) ToGCPIntegrationType() *types.GCPIntegration {
 	return &types.GCPIntegration{
+		CreatedAt:    g.CreatedAt,
 		ID:           g.ID,
 		UserID:       g.UserID,
 		ProjectID:    g.ProjectID,

+ 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
 	}
 }