Kaynağa Gözat

digitalocean with postrenderer support

Alexander Belanger 5 yıl önce
ebeveyn
işleme
e18b10b109

+ 47 - 0
cli/cmd/api/registry.go

@@ -106,6 +106,53 @@ func (c *Client) CreateGCR(
 	return bodyResp, nil
 }
 
+// CreateDOCRRequest represents the accepted fields for creating
+// a DOCR registry
+type CreateDOCRRequest struct {
+	Name            string `json:"name"`
+	DOIntegrationID uint   `json:"do_integration_id"`
+	URL             string `json:"url"`
+}
+
+// CreateDOCRResponse is the resulting registry after creation
+type CreateDOCRResponse models.RegistryExternal
+
+// CreateDOCR creates an Digital Ocean Container Registry integration
+func (c *Client) CreateDOCR(
+	ctx context.Context,
+	projectID uint,
+	createDOCR *CreateDOCRRequest,
+) (*CreateDOCRResponse, error) {
+	data, err := json.Marshal(createDOCR)
+
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest(
+		"POST",
+		fmt.Sprintf("%s/projects/%d/registries", c.BaseURL, projectID),
+		strings.NewReader(string(data)),
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	req = req.WithContext(ctx)
+	bodyResp := &CreateDOCRResponse{}
+
+	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
+		if httpErr != nil {
+			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
+		}
+
+		return nil, err
+	}
+
+	return bodyResp, nil
+}
+
 // ListRegistryResponse is the list of registries for a project
 type ListRegistryResponse []models.RegistryExternal
 

+ 7 - 8
cli/cmd/connect.go

@@ -59,7 +59,7 @@ var connectDOCRCmd = &cobra.Command{
 	Use:   "docr",
 	Short: "Adds a DOCR instance to a project",
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, runConnectGCR)
+		err := checkLoginAndRun(args, runConnectDOCR)
 
 		if err != nil {
 			os.Exit(1)
@@ -115,6 +115,7 @@ func init() {
 
 	connectCmd.AddCommand(connectECRCmd)
 	connectCmd.AddCommand(connectGCRCmd)
+	connectCmd.AddCommand(connectDOCRCmd)
 	connectCmd.AddCommand(connectHRCmd)
 }
 
@@ -167,18 +168,16 @@ func runConnectGCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) err
 }
 
 func runConnectDOCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
-	_, err := connect.DOCR(
+	regID, err := connect.DOCR(
 		client,
 		getProjectID(),
 	)
 
-	return err
-
-	// if err != nil {
-	// 	return err
-	// }
+	if err != nil {
+		return err
+	}
 
-	// return setRegistry(regID)
+	return setRegistry(regID)
 }
 
 func runConnectHelmRepoBasic(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {

+ 45 - 38
cli/cmd/connect/docr.go

@@ -3,8 +3,12 @@ package connect
 import (
 	"context"
 	"fmt"
+	"strings"
 
 	"github.com/porter-dev/porter/cli/cmd/api"
+	"github.com/porter-dev/porter/cli/cmd/utils"
+
+	ints "github.com/porter-dev/porter/internal/models/integrations"
 )
 
 // DOCR creates a DOCR integration
@@ -24,51 +28,54 @@ func DOCR(
 		return 0, err
 	}
 
-	fmt.Println(oauthInts)
-
-	// 	userResp, err := utils.PromptPlaintext(
-	// 		fmt.Sprintf(`Porter can set up an IAM user in your AWS account to connect to this ECR instance automatically.
-	// Would you like to proceed? %s `,
-	// 			color.New(color.FgCyan).Sprintf("[y/n]"),
-	// 		),
-	// 	)
-
-	// 	if err != nil {
-	// 		return 0, err
-	// 	}
-
-	// 	if userResp := strings.ToLower(userResp); userResp == "y" || userResp == "yes" {
-	// 		agent := awsLocal.NewDefaultAgent()
+	linkedDO := false
+	var doAuth ints.OAuthIntegrationExternal
 
-	// 		creds, err := agent.CreateIAMECRUser(region)
+	// iterate through oauth integrations to find do
+	for _, oauthInt := range oauthInts {
+		if oauthInt.Client == ints.OAuthDigitalOcean {
+			linkedDO = true
+			doAuth = oauthInt
+			break
+		}
+	}
 
-	// 		if err != nil {
-	// 			color.New(color.FgRed).Printf("Automatic creation failed, manual input required. Error was: %v\n", err)
-	// 			return ecrManual(client, projectID, region)
-	// 		}
+	if !linkedDO {
+		doAuth, err = triggerDigitalOceanOAuth(projectID)
 
-	// 		waitForAuthorizationToken(region, creds)
+		if err != nil {
+			return 0, err
+		}
+	}
 
-	// 		integration, err := client.CreateAWSIntegration(
-	// 			context.Background(),
-	// 			projectID,
-	// 			&api.CreateAWSIntegrationRequest{
-	// 				AWSAccessKeyID:     creds.AWSAccessKeyID,
-	// 				AWSSecretAccessKey: creds.AWSSecretAccessKey,
-	// 				AWSRegion:          region,
-	// 			},
-	// 		)
+	// use the digital ocean oauth to create a registry
+	regURL, err := utils.PromptPlaintext(fmt.Sprintf(`Please provide the registry URL, in the form registry.digitalocean.com/[REGISTRY_NAME]. For example, registry.digitalocean.com/porter-test. 
+Registry URL: `))
 
-	// 		if err != nil {
-	// 			return 0, err
-	// 		}
+	if err != nil {
+		return 0, err
+	}
 
-	// 		color.New(color.FgGreen).Printf("created aws integration with id %d\n", integration.ID)
+	urlArr := strings.Split(regURL, "/")
+	regName := urlArr[len(urlArr)-1]
 
-	// 		return linkRegistry(client, projectID, integration.ID)
-	// 	}
+	if err != nil {
+		return 0, err
+	}
 
-	// 	return ecrManual(client, projectID, region)
+	reg, err := client.CreateDOCR(
+		context.Background(),
+		projectID,
+		&api.CreateDOCRRequest{
+			Name:            regName,
+			DOIntegrationID: doAuth.ID,
+			URL:             regURL,
+		},
+	)
+
+	return reg.ID, nil
+}
 
-	return 0, nil
+func triggerDigitalOceanOAuth(projectID uint) (ints.OAuthIntegrationExternal, error) {
+	return ints.OAuthIntegrationExternal{}, nil
 }

+ 43 - 12
cli/cmd/docker.go

@@ -6,9 +6,11 @@ import (
 	"io/ioutil"
 	"net/url"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strings"
 
+	"github.com/fatih/color"
 	"github.com/porter-dev/porter/cli/cmd/api"
 	"github.com/porter-dev/porter/cli/cmd/github"
 	"github.com/spf13/cobra"
@@ -93,21 +95,30 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 		return err
 	}
 
-	// download the porter cred helper
-	z := &github.ZIPReleaseGetter{
-		AssetName:           "docker-credential-porter",
-		AssetFolderDest:     "/usr/local/bin",
-		ZipFolderDest:       filepath.Join(home, ".porter"),
-		ZipName:             "docker-credential-porter_latest.zip",
-		EntityID:            "porter-dev",
-		RepoName:            "porter",
-		IsPlatformDependent: true,
+	// check if the docker credential helper exists
+	if !commandExists("docker-credential-porter") {
+		err := downloadCredMatchingRelease()
+
+		if err != nil {
+			color.New(color.FgRed).Println("Failed to download credential helper binary:", err.Error())
+			os.Exit(1)
+		}
 	}
 
-	err = z.GetLatestRelease()
+	// otherwise, check the version flag of the binary
+	cmdVersionCred := exec.Command("docker-credential-porter", "--version")
+	writer := &versionWriter{}
+	cmdVersionCred.Stdout = writer
 
-	if err != nil {
-		return err
+	err = cmdVersionCred.Run()
+
+	if err != nil || writer.Version != Version {
+		err := downloadCredMatchingRelease()
+
+		if err != nil {
+			color.New(color.FgRed).Println("Failed to download credential helper binary:", err.Error())
+			os.Exit(1)
+		}
 	}
 
 	config := &configfile.ConfigFile{
@@ -126,3 +137,23 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 
 	return config.Save()
 }
+
+func downloadCredMatchingRelease() error {
+	// download the porter cred helper
+	z := &github.ZIPReleaseGetter{
+		AssetName:           "docker-credential-porter",
+		AssetFolderDest:     "/usr/local/bin",
+		ZipFolderDest:       filepath.Join(home, ".porter"),
+		ZipName:             "docker-credential-porter_latest.zip",
+		EntityID:            "porter-dev",
+		RepoName:            "porter",
+		IsPlatformDependent: true,
+	}
+
+	return z.GetRelease(Version)
+}
+
+func commandExists(cmd string) bool {
+	_, err := exec.LookPath(cmd)
+	return err == nil
+}

+ 1 - 1
cli/cmd/server.go

@@ -270,7 +270,7 @@ func downloadMatchingRelease(porterDir string) error {
 		IsPlatformDependent: false,
 	}
 
-	return zStatic.GetLatestRelease()
+	return zStatic.GetRelease(Version)
 }
 
 type versionWriter struct {

+ 1 - 1
cli/cmd/version.go

@@ -7,7 +7,7 @@ import (
 )
 
 // Version will be linked by an ldflag during build
-var Version string = "v0.1.0-beta.3.1"
+var Version string = "dev"
 
 var versionCmd = &cobra.Command{
 	Use:     "version",

+ 20 - 0
cmd/docker-credential-porter/helper/helper.go

@@ -102,6 +102,10 @@ func (p *PorterHelper) getDOCR(serverURL string) (user string, secret string, er
 	urlP, err := url.Parse(serverURL)
 
 	if err != nil {
+		if p.Debug {
+			log.Printf("Error: %s\n", err.Error())
+		}
+
 		return "", "", err
 	}
 
@@ -110,20 +114,36 @@ func (p *PorterHelper) getDOCR(serverURL string) (user string, secret string, er
 
 	var token string
 
+	if p.Debug {
+		log.Printf("GETTING FROM DOCR", urlP)
+	}
+
 	if cachedEntry != nil && cachedEntry.IsValid(time.Now()) {
 		token = cachedEntry.AuthorizationToken
+
+		if p.Debug {
+			log.Printf("USING CACHED TOKEN", token)
+		}
 	} else {
 		host := viper.GetString("host")
 		projID := viper.GetUint("project")
 
 		client := api.NewClient(host+"/api", "cookie.json")
 
+		if p.Debug {
+			log.Printf("MAKING REQUEST", host, projID)
+		}
+
 		// get a token from the server
 		tokenResp, err := client.GetDOCRAuthorizationToken(context.Background(), projID, &api.GetDOCRTokenRequest{
 			ServerURL: serverURL,
 		})
 
 		if err != nil {
+			if p.Debug {
+				log.Printf("Error: %s\n", err.Error())
+			}
+
 			return "", "", err
 		}
 

+ 2 - 0
internal/forms/registry.go

@@ -14,6 +14,7 @@ type CreateRegistry struct {
 	URL              string `json:"url"`
 	GCPIntegrationID uint   `json:"gcp_integration_id"`
 	AWSIntegrationID uint   `json:"aws_integration_id"`
+	DOIntegrationID  uint   `json:"do_integration_id"`
 }
 
 // ToRegistry converts the form to a gorm registry model
@@ -24,6 +25,7 @@ func (cr *CreateRegistry) ToRegistry(repo repository.Repository) (*models.Regist
 		URL:              cr.URL,
 		GCPIntegrationID: cr.GCPIntegrationID,
 		AWSIntegrationID: cr.AWSIntegrationID,
+		DOIntegrationID:  cr.DOIntegrationID,
 	}
 
 	if registry.URL == "" && registry.AWSIntegrationID != 0 {

+ 1 - 1
internal/helm/postrenderer.go

@@ -70,7 +70,7 @@ func NewDockerSecretsPostRenderer(
 		addReg := parsedRegURL.Host
 
 		if parsedRegURL.Path != "" {
-			addReg += "/" + parsedRegURL.Path
+			addReg += "/" + strings.Trim(parsedRegURL.Path, "/")
 		}
 
 		registries[addReg] = reg

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

@@ -13,6 +13,7 @@ const (
 	Kube     IntegrationService = "kube"
 	GCR      IntegrationService = "gcr"
 	ECR      IntegrationService = "ecr"
+	DOCR     IntegrationService = "docr"
 	Github   IntegrationService = "github"
 	Docker   IntegrationService = "docker"
 )

+ 2 - 0
internal/models/registry.go

@@ -62,6 +62,8 @@ func (r *Registry) Externalize() *RegistryExternal {
 		serv = integrations.ECR
 	} else if r.GCPIntegrationID != 0 {
 		serv = integrations.GCR
+	} else if r.DOIntegrationID != 0 {
+		serv = integrations.DOCR
 	}
 
 	return &RegistryExternal{

+ 17 - 7
internal/registry/registry.go

@@ -528,7 +528,9 @@ func (r *Registry) getECRDockerConfigFile(
 func (r *Registry) getGCRDockerConfigFile(
 	repo repository.Repository,
 ) (*configfile.ConfigFile, error) {
-	token, err := r.GetGCRToken(repo)
+	gcp, err := repo.GCPIntegration.ReadGCPIntegration(
+		r.GCPIntegrationID,
+	)
 
 	if err != nil {
 		return nil, err
@@ -540,12 +542,14 @@ func (r *Registry) getGCRDockerConfigFile(
 		key = "https://" + key
 	}
 
+	parsedURL, _ := url.Parse(key)
+
 	return &configfile.ConfigFile{
 		AuthConfigs: map[string]types.AuthConfig{
-			key: types.AuthConfig{
-				Username: "oauth2accesstoken",
-				Password: string(token.Token),
-				Auth:     string(token.Token),
+			parsedURL.Host: types.AuthConfig{
+				Username: "_json_key",
+				Password: string(gcp.GCPKeyData),
+				Auth:     generateAuthToken("_json_key", string(gcp.GCPKeyData)),
 			},
 		},
 	}, nil
@@ -575,13 +579,19 @@ func (r *Registry) getDOCRDockerConfigFile(
 		key = "https://" + key
 	}
 
+	parsedURL, _ := url.Parse(key)
+
 	return &configfile.ConfigFile{
 		AuthConfigs: map[string]types.AuthConfig{
-			key: types.AuthConfig{
+			parsedURL.Host: types.AuthConfig{
 				Username: tok,
 				Password: tok,
-				Auth:     tok,
+				Auth:     generateAuthToken(tok, tok),
 			},
 		},
 	}, nil
 }
+
+func generateAuthToken(username, password string) string {
+	return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
+}

+ 16 - 1
server/router/router.go

@@ -596,7 +596,12 @@ func New(a *api.App) *chi.Mux {
 			auth.DoesUserHaveProjectAccess(
 				auth.DoesUserHaveAWSIntegrationAccess(
 					auth.DoesUserHaveGCPIntegrationAccess(
-						requestlog.NewHandler(a.HandleCreateRegistry, l),
+						auth.DoesUserHaveDOIntegrationAccess(
+							requestlog.NewHandler(a.HandleCreateRegistry, l),
+							mw.URLParam,
+							mw.BodyParam,
+							true,
+						),
 						mw.URLParam,
 						mw.BodyParam,
 						true,
@@ -654,6 +659,16 @@ func New(a *api.App) *chi.Mux {
 			),
 		)
 
+		r.Method(
+			"GET",
+			"/projects/{project_id}/registries/docr/token",
+			auth.DoesUserHaveProjectAccess(
+				requestlog.NewHandler(a.HandleGetProjectRegistryDOCRToken, l),
+				mw.URLParam,
+				mw.WriteAccess,
+			),
+		)
+
 		r.Method(
 			"DELETE",
 			"/projects/{project_id}/registries/{registry_id}",