diff options
author | Martin Polden <mpolden@mpolden.no> | 2021-08-31 11:14:17 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2021-08-31 11:16:28 +0200 |
commit | be8b4d737f8b1aa5f2c2dd102eef15043a759765 (patch) | |
tree | 58b1f734917708b5c650f4a18a3ddc38f993017e /client | |
parent | a4a9b847b86281c1bf057fee935ae9a897065c8c (diff) |
Implement cloud deployment
Diffstat (limited to 'client')
-rw-r--r-- | client/go/cmd/cert.go | 24 | ||||
-rw-r--r-- | client/go/cmd/deploy.go | 32 | ||||
-rw-r--r-- | client/go/cmd/target.go | 2 | ||||
-rw-r--r-- | client/go/vespa/deploy.go | 39 |
4 files changed, 50 insertions, 47 deletions
diff --git a/client/go/cmd/cert.go b/client/go/cmd/cert.go index f76a7041a82..c47b991881f 100644 --- a/client/go/cmd/cert.go +++ b/client/go/cmd/cert.go @@ -4,11 +4,13 @@ package cmd import ( + "fmt" "log" "os" "path/filepath" "github.com/spf13/cobra" + "github.com/vespa-engine/vespa/util" "github.com/vespa-engine/vespa/vespa" ) @@ -40,24 +42,26 @@ var certCmd = &cobra.Command{ app, err := vespa.ApplicationFromString(applicationArg) fatalIfErr(err) - pkg, err := vespa.ApplicationPackageFrom(path) fatalIfErr(err) - if pkg.HasCertificate() && !overwriteCertificate { - log.Print("Certificate already exists. Use -f option to recreate") - return - } - configDir, err := configDir(app.String()) fatalIfErr(err) + securityDir := filepath.Join(pkg.Path, "security") pkgCertificateFile := filepath.Join(securityDir, "clients.pem") privateKeyFile := filepath.Join(configDir, "data-plane-private-key.pem") certificateFile := filepath.Join(configDir, "data-plane-public-cert.pem") + if !overwriteCertificate { + for _, file := range []string{pkgCertificateFile, privateKeyFile, certificateFile} { + if util.PathExists(file) { + errorWithHint(fmt.Errorf("Certificate or private key %s already exists", color.Cyan(file)), "Use -f flag to force overwriting") + return + } + } + } keyPair, err := vespa.CreateKeyPair() fatalIfErr(err) - err = os.MkdirAll(configDir, 0755) fatalIfErr(err) err = os.MkdirAll(securityDir, 0755) @@ -69,9 +73,9 @@ var certCmd = &cobra.Command{ err = keyPair.WritePrivateKeyFile(privateKeyFile, overwriteCertificate) fatalIfErr(err) - log.Printf("Certificate written to %s", color.Green(pkgCertificateFile)) - log.Printf("Certificate written to %s", color.Green(certificateFile)) - log.Printf("Private key written to %s", color.Green(privateKeyFile)) + log.Printf("Certificate written to %s", color.Cyan(pkgCertificateFile)) + log.Printf("Certificate written to %s", color.Cyan(certificateFile)) + log.Printf("Private key written to %s", color.Cyan(privateKeyFile)) }, } diff --git a/client/go/cmd/deploy.go b/client/go/cmd/deploy.go index 9c867241d52..a3f209ef5df 100644 --- a/client/go/cmd/deploy.go +++ b/client/go/cmd/deploy.go @@ -49,16 +49,18 @@ var deployCmd = &cobra.Command{ var err error d.Zone, err = vespa.ZoneFromString(zoneArg) if err != nil { - log.Fatal(err) + errorWithHint(err, "Zones have the format <env>.<region>.") + return } d.Application, err = vespa.ApplicationFromString(applicationArg) if err != nil { - log.Fatal(err) + errorWithHint(err, "Applications have the format <tenant>.<application-name>.<instance-name>") + return } - - d.KeyPair, err = loadApplicationKeyPair(applicationArg) + d.APIKey, err = loadApiKey(applicationArg) if err != nil { - log.Fatal(err) + errorWithHint(err, "Deployment to cloud requires an API key. Try 'vespa api-key'") + return } } resolvedSrc, err := vespa.Deploy(d) @@ -98,23 +100,25 @@ var activateCmd = &cobra.Command{ }, } -func loadApplicationKeyPair(application string) (vespa.PemKeyPair, error) { +func loadApiKey(application string) ([]byte, error) { configDir, err := configDir(application) if err != nil { - return vespa.PemKeyPair{}, err + return nil, err } - certificateFile := filepath.Join(configDir, "data-plane-public-cert.pem") - privateKeyFile := filepath.Join(configDir, "data-plane-private-key.pem") - return vespa.LoadKeyPair(privateKeyFile, certificateFile) + apiKeyPath := filepath.Join(configDir, "api-key.pem") + return os.ReadFile(apiKeyPath) } func applicationSource(args []string) string { if len(args) > 0 { return args[0] } - wd, err := os.Getwd() - if err != nil { - log.Fatalf("Could not determine working directory: %s", err) + return "." +} + +func errorWithHint(err error, hints ...string) { + log.Print(color.Red("Error:"), err) + for _, hint := range hints { + log.Print(color.Cyan("Hint: "), hint) } - return wd } diff --git a/client/go/cmd/target.go b/client/go/cmd/target.go index d9acddc4dc9..7bb7a61a394 100644 --- a/client/go/cmd/target.go +++ b/client/go/cmd/target.go @@ -13,7 +13,7 @@ import ( const ( targetFlag = "target" - cloudApi = "https://api.vespa-external.aws.oath.cloud" + cloudApi = "https://api.vespa-external.aws.oath.cloud:4443" ) var targetArg string diff --git a/client/go/vespa/deploy.go b/client/go/vespa/deploy.go index 9955c3dfbff..44e67ece1e7 100644 --- a/client/go/vespa/deploy.go +++ b/client/go/vespa/deploy.go @@ -39,7 +39,6 @@ type Deployment struct { TargetURL string Application ApplicationID Zone ZoneID - KeyPair PemKeyPair APIKey []byte } @@ -51,6 +50,10 @@ func (a ApplicationID) String() string { return fmt.Sprintf("%s.%s.%s", a.Tenant, a.Application, a.Instance) } +func (a ApplicationID) SerializedForm() string { + return fmt.Sprintf("%s:%s:%s", a.Tenant, a.Application, a.Instance) +} + func (d Deployment) String() string { return fmt.Sprintf("deployment of %s to %s target", d.Application, d.TargetType) } @@ -59,13 +62,6 @@ func (d *Deployment) IsCloud() bool { return d.TargetType == "cloud" } func (ap *ApplicationPackage) IsZip() bool { return isZip(ap.Path) } -func (ap *ApplicationPackage) HasCertificate() bool { - if ap.IsZip() { - return true // TODO: Consider looking inside zip to verify - } - return util.PathExists(filepath.Join(ap.Path, "security", "clients.pem")) -} - func (ap *ApplicationPackage) zipReader() (io.ReadCloser, error) { zipFile := ap.Path if !ap.IsZip() { @@ -134,7 +130,7 @@ func Prepare(deployment Deployment) (string, error) { if err != nil { return "", err } - return deploy(u, deployment.ApplicationSource) + return deploy(u, deployment) } func Activate(deployment Deployment) (string, error) { @@ -147,7 +143,7 @@ func Activate(deployment Deployment) (string, error) { if err != nil { return "", err } - return deploy(u, deployment.ApplicationSource) + return deploy(u, deployment) } func Deploy(deployment Deployment) (string, error) { @@ -156,12 +152,6 @@ func Deploy(deployment Deployment) (string, error) { if deployment.APIKey == nil { return "", fmt.Errorf("%s: missing api key", deployment.String()) } - if deployment.KeyPair.Certificate == nil { - return "", fmt.Errorf("%s: missing certificate", deployment) - } - if deployment.KeyPair.PrivateKey == nil { - return "", fmt.Errorf("%s: missing private key", deployment) - } if deployment.Zone.Environment == "" || deployment.Zone.Region == "" { return "", fmt.Errorf("%s: missing zone", deployment) } @@ -171,17 +161,16 @@ func Deploy(deployment Deployment) (string, error) { deployment.Application.Instance, deployment.Zone.Environment, deployment.Zone.Region) - return "", fmt.Errorf("cloud deployment is not implemented") } u, err := url.Parse(deployment.TargetURL + path) if err != nil { return "", err } - return deploy(u, deployment.ApplicationSource) + return deploy(u, deployment) } -func deploy(url *url.URL, applicationSource string) (string, error) { - pkg, err := ApplicationPackageFrom(applicationSource) +func deploy(url *url.URL, deployment Deployment) (string, error) { + pkg, err := ApplicationPackageFrom(deployment.ApplicationSource) if err != nil { return "", err } @@ -189,13 +178,13 @@ func deploy(url *url.URL, applicationSource string) (string, error) { if err != nil { return "", err } - if err := postApplicationPackage(url, zipReader); err != nil { + if err := postApplicationPackage(url, zipReader, deployment); err != nil { return "", err } return pkg.Path, nil } -func postApplicationPackage(url *url.URL, zipReader io.Reader) error { +func postApplicationPackage(url *url.URL, zipReader io.Reader, deployment Deployment) error { header := http.Header{} header.Add("Content-Type", "application/zip") request := &http.Request{ @@ -204,6 +193,12 @@ func postApplicationPackage(url *url.URL, zipReader io.Reader) error { Header: header, Body: io.NopCloser(zipReader), } + if deployment.APIKey != nil { + signer := NewRequestSigner(deployment.Application.SerializedForm(), deployment.APIKey) + if err := signer.SignRequest(request); err != nil { + return err + } + } serviceDescription := "Deploy service" response, err := util.HttpDo(request, time.Minute*10, serviceDescription) if err != nil { |