summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-08-31 11:14:17 +0200
committerMartin Polden <mpolden@mpolden.no>2021-08-31 11:16:28 +0200
commitbe8b4d737f8b1aa5f2c2dd102eef15043a759765 (patch)
tree58b1f734917708b5c650f4a18a3ddc38f993017e /client
parenta4a9b847b86281c1bf057fee935ae9a897065c8c (diff)
Implement cloud deployment
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/cert.go24
-rw-r--r--client/go/cmd/deploy.go32
-rw-r--r--client/go/cmd/target.go2
-rw-r--r--client/go/vespa/deploy.go39
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 {