aboutsummaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-09-02 16:41:31 +0200
committerMartin Polden <mpolden@mpolden.no>2021-09-02 16:55:12 +0200
commit9caf2b84e713f4feb5a420b64cefd3d9c9f543e8 (patch)
treedf333ab3e60f53ac3c2a7feea3092343dd882f13 /client
parent2b4612bfa03ef5c34f6af355add448f8d573f330 (diff)
Use new target mechanism
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/deploy.go100
-rw-r--r--client/go/cmd/deploy_test.go4
-rw-r--r--client/go/cmd/document.go9
-rw-r--r--client/go/cmd/document_test.go4
-rw-r--r--client/go/cmd/query.go4
-rw-r--r--client/go/cmd/query_test.go4
-rw-r--r--client/go/cmd/root.go2
-rw-r--r--client/go/cmd/status.go38
-rw-r--r--client/go/cmd/status_test.go2
-rw-r--r--client/go/cmd/target.go102
-rw-r--r--client/go/vespa/deploy.go76
11 files changed, 166 insertions, 179 deletions
diff --git a/client/go/cmd/deploy.go b/client/go/cmd/deploy.go
index 41702b8a977..155857bcc37 100644
--- a/client/go/cmd/deploy.go
+++ b/client/go/cmd/deploy.go
@@ -47,41 +47,21 @@ has started but may not have completed.`,
printErr(nil, err.Error())
return
}
- d := vespa.DeploymentOpts{
- ApplicationPackage: pkg,
- TargetType: getTargetType(),
- TargetURL: deployTarget(),
- }
- if d.IsCloud() {
- var err error
- d.Zone, err = vespa.ZoneFromString(zoneArg)
- if err != nil {
- printErrHint(err, "Zones have the format <env>.<region>.")
- return
- }
- d.Application, err = vespa.ApplicationFromString(getApplication())
- if err != nil {
- printErrHint(err, "Applications have the format <tenant>.<application-name>.<instance-name>")
- return
- }
- if !d.ApplicationPackage.HasCertificate() {
+ target := getTarget()
+ opts := vespa.DeploymentOpts{ApplicationPackage: pkg, Target: target}
+ if opts.IsCloud() {
+ deployment := deploymentFromArgs()
+ if !opts.ApplicationPackage.HasCertificate() {
printErrHint(fmt.Errorf("Missing certificate in application package"), "Applications in Vespa Cloud require a certificate", "Try 'vespa cert'")
- return
- }
- configDir := configDir("")
- if configDir == "" {
- return
- }
- d.APIKey = readAPIKey(configDir, d.Application.Tenant)
- if d.APIKey == nil {
- printErrHint(err, "Deployment to cloud requires an API key. Try 'vespa api-key'")
- return
}
+ opts.APIKey = readAPIKey(deployment.Application.Tenant)
+ opts.Deployment = deployment
}
- if err := vespa.Deploy(d); err == nil {
+ if err := vespa.Deploy(opts); err == nil {
printSuccess("Deployed ", color.Cyan(pkg.Path))
- if d.IsCloud() {
- log.Print("See ", color.Cyan(fmt.Sprintf("https://console.vespa.oath.cloud/tenant/%s/application/%s/dev/instance/%s", d.Application.Tenant, d.Application.Application, d.Application.Instance)), " for deployment status")
+ if opts.IsCloud() {
+ log.Printf("\n\nUse %s for deployment status, or see", color.Cyan("vespa status"))
+ log.Print(color.Cyan(fmt.Sprintf("https://console.vespa.oath.cloud/tenant/%s/application/%s/dev/instance/%s", opts.Deployment.Application.Tenant, opts.Deployment.Application.Application, opts.Deployment.Application.Instance)))
}
} else {
printErr(nil, err.Error())
@@ -103,16 +83,13 @@ var prepareCmd = &cobra.Command{
if configDir == "" {
return
}
+ target := getTarget()
sessionID, err := vespa.Prepare(vespa.DeploymentOpts{
ApplicationPackage: pkg,
- TargetType: getTargetType(),
- TargetURL: deployTarget(),
+ Target: target,
})
if err == nil {
- if err := writeSessionID(configDir, sessionID); err != nil {
- printErr(err, "Could not write session ID")
- return
- }
+ writeSessionID(configDir, sessionID)
printSuccess("Prepared ", color.Cyan(pkg.Path), " with session ", sessionID)
} else {
printErr(nil, err.Error())
@@ -131,18 +108,11 @@ var activateCmd = &cobra.Command{
return
}
configDir := configDir("default")
- if configDir == "" {
- return
- }
- sessionID, err := readSessionID(configDir)
- if err != nil {
- printErr(err, "Could not read session ID")
- return
- }
+ sessionID := readSessionID(configDir)
+ target := getTarget()
err = vespa.Activate(sessionID, vespa.DeploymentOpts{
ApplicationPackage: pkg,
- TargetType: getTargetType(),
- TargetURL: deployTarget(),
+ Target: target,
})
if err == nil {
printSuccess("Activated ", color.Cyan(pkg.Path), " with session ", sessionID)
@@ -152,33 +122,51 @@ var activateCmd = &cobra.Command{
},
}
-func writeSessionID(appConfigDir string, sessionID int64) error {
+func writeSessionID(appConfigDir string, sessionID int64) {
if err := os.MkdirAll(appConfigDir, 0755); err != nil {
- return err
+ printErr(err, "Could not create directory for session ID")
+ }
+ if err := os.WriteFile(sessionIDFile(appConfigDir), []byte(fmt.Sprintf("%d\n", sessionID)), 0600); err != nil {
+ printErr(err, "Could not write session ID")
}
- return os.WriteFile(sessionIDFile(appConfigDir), []byte(fmt.Sprintf("%d\n", sessionID)), 0600)
}
-func readSessionID(appConfigDir string) (int64, error) {
+func readSessionID(appConfigDir string) int64 {
b, err := os.ReadFile(sessionIDFile(appConfigDir))
if err != nil {
- return 0, err
+ printErr(err, "Could not read session ID")
}
- return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64)
+ id, err := strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64)
+ if err != nil {
+ printErr(err, "Invalid session ID")
+ }
+ return id
}
func sessionIDFile(appConfigDir string) string { return filepath.Join(appConfigDir, "session_id") }
-func readAPIKey(configDir, tenant string) []byte {
+func readAPIKey(tenant string) []byte {
+ configDir := configDir("")
apiKeyPath := filepath.Join(configDir, tenant+".api-key.pem")
key, err := os.ReadFile(apiKeyPath)
if err != nil {
- printErr(err, "Could not read API key from ", color.Cyan(apiKeyPath))
- return nil
+ printErrHint(err, "Deployment to cloud requires an API key. Try 'vespa api-key'")
}
return key
}
+func deploymentFromArgs() vespa.Deployment {
+ zone, err := vespa.ZoneFromString(zoneArg)
+ if err != nil {
+ printErrHint(err, "Zone format is <env>.<region>")
+ }
+ app, err := vespa.ApplicationFromString(getApplication())
+ if err != nil {
+ printErrHint(err, "Application format is <tenant>.<app>.<instance>")
+ }
+ return vespa.Deployment{Application: app, Zone: zone}
+}
+
func applicationSource(args []string) string {
if len(args) > 0 {
return args[0]
diff --git a/client/go/cmd/deploy_test.go b/client/go/cmd/deploy_test.go
index 0a8db5f4fd2..5b6abcc0ce3 100644
--- a/client/go/cmd/deploy_test.go
+++ b/client/go/cmd/deploy_test.go
@@ -117,9 +117,7 @@ func assertActivate(applicationPackage string, arguments []string, t *testing.T)
client := &mockHttpClient{}
configDir := t.TempDir()
appConfigDir := filepath.Join(configDir, ".vespa", "default")
- if err := writeSessionID(appConfigDir, 42); err != nil {
- t.Fatal(err)
- }
+ writeSessionID(appConfigDir, 42)
assert.Equal(t,
"Success: Activated "+applicationPackage+" with session 42\n",
execute(command{args: arguments, configDir: configDir}, t, client))
diff --git a/client/go/cmd/document.go b/client/go/cmd/document.go
index d4ca3119e0e..e4591886511 100644
--- a/client/go/cmd/document.go
+++ b/client/go/cmd/document.go
@@ -44,8 +44,7 @@ should be used instead of this.`,
var documentPutCmd = &cobra.Command{
Use: "put [<id>] <json-file>",
Short: "Writes a document to Vespa",
- Long: `Writes a document to Vespa.
-
+ Long: `Writes the document in the given file to Vespa.
If the document already exists, all its values will be replaced by this document.
If the document id is specified both as an argument and in the file the argument takes precedence.`,
Args: cobra.RangeArgs(1, 2),
@@ -64,7 +63,6 @@ var documentUpdateCmd = &cobra.Command{
Use: "update [<id>] <json-file>",
Short: "Modifies some fields of an existing document",
Long: `Updates the values of the fields given in a json file as specified in the file.
-
If the document id is specified both as an argument and in the file the argument takes precedence.`,
Args: cobra.RangeArgs(1, 2),
Example: `$ vespa document update src/test/resources/A-Head-Full-of-Dreams-Update.json
@@ -82,7 +80,6 @@ var documentRemoveCmd = &cobra.Command{
Use: "remove <id or json.file>",
Short: "Removes a document from Vespa",
Long: `Removes the document specified either as a document id or given in the json file.
-
If the document id is specified both as an argument and in the file the argument takes precedence.`,
Args: cobra.ExactArgs(1),
Example: `$ vespa document remove src/test/resources/A-Head-Full-of-Dreams-Remove.json
@@ -105,6 +102,10 @@ var documentGetCmd = &cobra.Command{
},
}
+func documentTarget() string {
+ return getService("document").BaseURL
+}
+
func printResult(result util.OperationResult, payloadOnlyOnSuccess bool) {
if !result.Success {
log.Print(color.Red("Error: "), result.Message)
diff --git a/client/go/cmd/document_test.go b/client/go/cmd/document_test.go
index 1fc767c1858..5f3edeee8a0 100644
--- a/client/go/cmd/document_test.go
+++ b/client/go/cmd/document_test.go
@@ -93,7 +93,7 @@ func assertDocumentSend(arguments []string, expectedMethod string, expectedDocum
assert.Equal(t,
"Success: Sent "+expectedDocumentId+"\n",
executeCommand(t, client, arguments, []string{}))
- target := getTarget(documentContext).document
+ target := getService("document").BaseURL
expectedPath, _ := vespa.IdToURLPath(expectedDocumentId)
assert.Equal(t, target+"/document/v1/"+expectedPath, client.lastRequest.URL.String())
assert.Equal(t, "application/json", client.lastRequest.Header.Get("Content-Type"))
@@ -115,7 +115,7 @@ func assertDocumentGet(arguments []string, documentId string, t *testing.T) {
}
`,
executeCommand(t, client, arguments, []string{}))
- target := getTarget(documentContext).document
+ target := getService("document").BaseURL
expectedPath, _ := vespa.IdToURLPath(documentId)
assert.Equal(t, target+"/document/v1/"+expectedPath, client.lastRequest.URL.String())
assert.Equal(t, "GET", client.lastRequest.Method)
diff --git a/client/go/cmd/query.go b/client/go/cmd/query.go
index 0e9b48464ad..957be8735df 100644
--- a/client/go/cmd/query.go
+++ b/client/go/cmd/query.go
@@ -69,3 +69,7 @@ func splitArg(argument string) (string, string) {
return argument[0:equalsIndex], argument[equalsIndex+1:]
}
}
+
+func queryTarget() string {
+ return getService("query").BaseURL
+}
diff --git a/client/go/cmd/query_test.go b/client/go/cmd/query_test.go
index 8b85b0a9b57..9bb5f07c3a1 100644
--- a/client/go/cmd/query_test.go
+++ b/client/go/cmd/query_test.go
@@ -49,7 +49,7 @@ func assertQuery(t *testing.T, expectedQuery string, query ...string) {
"{\n \"query\": \"result\"\n}\n",
executeCommand(t, client, []string{"query"}, query),
"query output")
- assert.Equal(t, getTarget(queryContext).query+"/search/"+expectedQuery, client.lastRequest.URL.String())
+ assert.Equal(t, getService("query").BaseURL+"/search/"+expectedQuery, client.lastRequest.URL.String())
}
func assertQueryNonJsonResult(t *testing.T, expectedQuery string, query ...string) {
@@ -58,7 +58,7 @@ func assertQueryNonJsonResult(t *testing.T, expectedQuery string, query ...strin
"query result\n",
executeCommand(t, client, []string{"query"}, query),
"query output")
- assert.Equal(t, getTarget(queryContext).query+"/search/"+expectedQuery, client.lastRequest.URL.String())
+ assert.Equal(t, getService("query").BaseURL+"/search/"+expectedQuery, client.lastRequest.URL.String())
}
func assertQueryError(t *testing.T, status int, errorMessage string) {
diff --git a/client/go/cmd/root.go b/client/go/cmd/root.go
index 84285d546ea..57a4ab7f3aa 100644
--- a/client/go/cmd/root.go
+++ b/client/go/cmd/root.go
@@ -30,6 +30,7 @@ Vespa documentation: https://docs.vespa.ai`,
color aurora.Aurora
targetArg string
applicationArg string
+ waitSecsArg int
)
const (
@@ -48,6 +49,7 @@ func init() {
cobra.OnInitialize(readConfig)
rootCmd.PersistentFlags().StringVarP(&targetArg, targetFlag, "t", "local", "The name or URL of the recipient of this command")
rootCmd.PersistentFlags().StringVarP(&applicationArg, applicationFlag, "a", "", "The application to manage")
+ rootCmd.PersistentFlags().IntVarP(&waitSecsArg, "wait", "w", 0, "Number of seconds to wait for a service to become ready")
bindFlagToConfig(targetFlag, rootCmd)
bindFlagToConfig(applicationFlag, rootCmd)
}
diff --git a/client/go/cmd/status.go b/client/go/cmd/status.go
index bc351d336d1..b0d299ecfac 100644
--- a/client/go/cmd/status.go
+++ b/client/go/cmd/status.go
@@ -5,10 +5,11 @@
package cmd
import (
+ "fmt"
"log"
+ "time"
"github.com/spf13/cobra"
- "github.com/vespa-engine/vespa/util"
)
func init() {
@@ -24,7 +25,7 @@ var statusCmd = &cobra.Command{
Example: `$ vespa status query`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
- status(queryTarget(), "Query API")
+ status("query", "Query API")
},
}
@@ -34,7 +35,7 @@ var statusQueryCmd = &cobra.Command{
Example: `$ vespa status query`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
- status(queryTarget(), "Query API")
+ status("query", "Query API")
},
}
@@ -44,7 +45,7 @@ var statusDocumentCmd = &cobra.Command{
Example: `$ vespa status document`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
- status(documentTarget(), "Document API")
+ status("document", "Document API")
},
}
@@ -54,24 +55,25 @@ var statusDeployCmd = &cobra.Command{
Example: `$ vespa status deploy`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
- status(deployTarget(), "Deploy API")
+ status("deploy", "Deploy API")
},
}
-func status(target string, description string) {
- path := "/ApplicationStatus"
- response, err := util.HttpGet(target, path, description)
- if err != nil {
- log.Print(description, " at ", color.Cyan(target), " is ", color.Red("not ready"))
- log.Print(color.Yellow(err))
- return
+func status(service string, description string) {
+ s := getService(service)
+ timeout := time.Duration(waitSecsArg) * time.Second
+ if timeout > 0 {
+ log.Printf("Waiting %d %s for service to become ready ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
}
- defer response.Body.Close()
-
- if response.StatusCode != 200 {
- log.Print(description, " at ", color.Cyan(target), " is ", color.Red("not ready"))
- log.Print(color.Yellow(response.Status))
+ status, err := s.Wait(timeout)
+ if status/100 == 2 {
+ log.Print(description, " at ", color.Cyan(s.BaseURL), " is ", color.Green("ready"))
} else {
- log.Print(description, " at ", color.Cyan(target), " is ", color.Green("ready"))
+ log.Print(description, " at ", color.Cyan(s.BaseURL), " is ", color.Red("not ready"))
+ if err == nil {
+ log.Print(color.Yellow(fmt.Sprintf("Status %d", status)))
+ } else {
+ log.Print(color.Yellow(err))
+ }
}
}
diff --git a/client/go/cmd/status_test.go b/client/go/cmd/status_test.go
index 848dab3db26..10b1950d5d1 100644
--- a/client/go/cmd/status_test.go
+++ b/client/go/cmd/status_test.go
@@ -48,7 +48,7 @@ func assertDeployStatus(target string, args []string, t *testing.T) {
"Deploy API at "+target+" is ready\n",
executeCommand(t, client, []string{"status", "deploy"}, args),
"vespa status config-server")
- assert.Equal(t, target+"/ApplicationStatus", client.lastRequest.URL.String())
+ assert.Equal(t, target+"/status.html", client.lastRequest.URL.String())
}
func assertQueryStatus(target string, args []string, t *testing.T) {
diff --git a/client/go/cmd/target.go b/client/go/cmd/target.go
index d6c01acf5b1..97ded047284 100644
--- a/client/go/cmd/target.go
+++ b/client/go/cmd/target.go
@@ -5,44 +5,20 @@
package cmd
import (
+ "crypto/tls"
+ "fmt"
"log"
+ "path/filepath"
"strings"
-)
-
-const (
- cloudApi = "https://api.vespa-external.aws.oath.cloud:4443"
-)
-
-type target struct {
- deploy string
- query string
- document string
-}
-
-type context int32
+ "time"
-const (
- deployContext context = 0
- queryContext context = 1
- documentContext context = 2
+ "github.com/vespa-engine/vespa/vespa"
)
-func deployTarget() string {
- return getTarget(deployContext).deploy
-}
-
-func queryTarget() string {
- return getTarget(queryContext).query
-}
-
-func documentTarget() string {
- return getTarget(documentContext).document
-}
-
func getApplication() string {
app, err := getOption(applicationFlag)
if err != nil {
- log.Fatalf("a valid application must be specified")
+ printErr(err, "A valid application must be specified")
}
return app
}
@@ -50,45 +26,47 @@ func getApplication() string {
func getTargetType() string {
target, err := getOption(targetFlag)
if err != nil {
- log.Fatalf("a valid target must be specified")
+ printErr(err, "A valid target must be specified")
}
return target
}
-func getTarget(targetContext context) *target {
- targetValue := getTargetType()
- if strings.HasPrefix(targetValue, "http") {
- // TODO: Add default ports if missing
- switch targetContext {
- case deployContext:
- return &target{
- deploy: targetValue,
- }
- case queryContext:
- return &target{
- query: targetValue,
- }
- case documentContext:
- return &target{
- document: targetValue,
- }
- }
+func getService(service string) *vespa.Service {
+ t := getTarget()
+ timeout := time.Duration(waitSecsArg) * time.Second
+ if timeout > 0 {
+ log.Printf("Waiting %d %s for service discovery to complete ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
}
-
- // Otherwise, target is a name
-
- if targetValue == "" || targetValue == "local" {
- return &target{
- deploy: "http://127.0.0.1:19071",
- query: "http://127.0.0.1:8080",
- document: "http://127.0.0.1:8080",
- }
+ if err := t.DiscoverServices(timeout); err != nil {
+ printErr(err, "Failed to discover services")
}
-
- if targetValue == "cloud" {
- return &target{deploy: cloudApi}
+ s, err := t.Service(service)
+ if err != nil {
+ printErr(err, "Invalid service")
}
+ return s
+}
- log.Printf("Unknown target '%s': Use %s, %s or an URL", color.Red(targetValue), color.Cyan("local"), color.Cyan("cloud"))
+func getTarget() vespa.Target {
+ targetType := getTargetType()
+ if strings.HasPrefix(targetType, "http") {
+ return vespa.CustomTarget(targetType)
+ }
+ switch targetType {
+ case "local":
+ return vespa.LocalTarget()
+ case "cloud":
+ deployment := deploymentFromArgs()
+ apiKey := readAPIKey(deployment.Application.Tenant)
+ configDir := configDir(deployment.Application.String())
+ privateKeyFile := filepath.Join(configDir, "data-plane-private-key.pem")
+ certificateFile := filepath.Join(configDir, "data-plane-public-cert.pem")
+ kp, err := tls.LoadX509KeyPair(certificateFile, privateKeyFile)
+ if err != nil {
+ printErr(err, "Could not read key pair")
+ }
+ return vespa.CloudTarget(deployment, kp, apiKey)
+ }
+ printErrHint(fmt.Errorf("Invalid target: %s", targetType), "Valid targets are 'local', 'cloud' or an URL")
return nil
}
diff --git a/client/go/vespa/deploy.go b/client/go/vespa/deploy.go
index 55db98269f7..e57f78d954f 100644
--- a/client/go/vespa/deploy.go
+++ b/client/go/vespa/deploy.go
@@ -34,12 +34,15 @@ type ZoneID struct {
Region string
}
+type Deployment struct {
+ Application ApplicationID
+ Zone ZoneID
+}
+
type DeploymentOpts struct {
ApplicationPackage ApplicationPackage
- TargetType string
- TargetURL string
- Application ApplicationID
- Zone ZoneID
+ Target Target
+ Deployment Deployment
APIKey []byte
}
@@ -55,11 +58,23 @@ 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 in %s", d.Application, d.Zone)
+}
+
func (d DeploymentOpts) String() string {
- return fmt.Sprintf("deployment of %s to %s target", d.Application, d.TargetType)
+ return fmt.Sprintf("%s to %s", d.Deployment, d.Target.Type())
}
-func (d *DeploymentOpts) IsCloud() bool { return d.TargetType == "cloud" }
+func (d *DeploymentOpts) IsCloud() bool { return d.Target.Type() == cloudTargetType }
+
+func (d *DeploymentOpts) url(path string) (*url.URL, error) {
+ service, err := d.Target.Service("deploy")
+ if err != nil {
+ return nil, err
+ }
+ return url.Parse(service.BaseURL + path)
+}
func (ap *ApplicationPackage) HasCertificate() bool {
if ap.IsZip() {
@@ -133,7 +148,7 @@ func Prepare(deployment DeploymentOpts) (int64, error) {
if deployment.IsCloud() {
return 0, fmt.Errorf("%s: prepare is not supported", deployment)
}
- sessionURL, err := url.Parse(deployment.TargetURL + "/application/v2/tenant/default/session")
+ sessionURL, err := deployment.url("/application/v2/tenant/default/session")
if err != nil {
return 0, err
}
@@ -141,7 +156,7 @@ func Prepare(deployment DeploymentOpts) (int64, error) {
if err != nil {
return 0, err
}
- prepareURL, err := url.Parse(fmt.Sprintf("%s/application/v2/tenant/default/session/%d/prepared", deployment.TargetURL, sessionID))
+ prepareURL, err := deployment.url(fmt.Sprintf("/application/v2/tenant/default/session/%d/prepared", sessionID))
if err != nil {
return 0, err
}
@@ -163,7 +178,7 @@ func Activate(sessionID int64, deployment DeploymentOpts) error {
if deployment.IsCloud() {
return fmt.Errorf("%s: activate is not supported", deployment)
}
- u, err := url.Parse(fmt.Sprintf("%s/application/v2/tenant/default/session/%d/active", deployment.TargetURL, sessionID))
+ u, err := deployment.url(fmt.Sprintf("/application/v2/tenant/default/session/%d/active", sessionID))
if err != nil {
return err
}
@@ -180,35 +195,35 @@ func Activate(sessionID int64, deployment DeploymentOpts) error {
return nil
}
-func Deploy(deployment DeploymentOpts) error {
+func Deploy(opts DeploymentOpts) error {
path := "/application/v2/tenant/default/prepareandactivate"
- if deployment.IsCloud() {
- if !deployment.ApplicationPackage.HasCertificate() {
- return fmt.Errorf("%s: missing certificate in package", deployment)
+ if opts.IsCloud() {
+ if !opts.ApplicationPackage.HasCertificate() {
+ return fmt.Errorf("%s: missing certificate in package", opts)
}
- if deployment.APIKey == nil {
- return fmt.Errorf("%s: missing api key", deployment.String())
+ if opts.APIKey == nil {
+ return fmt.Errorf("%s: missing api key", opts.String())
}
- if deployment.Zone.Environment == "" || deployment.Zone.Region == "" {
- return fmt.Errorf("%s: missing zone", deployment)
+ if opts.Deployment.Zone.Environment == "" || opts.Deployment.Zone.Region == "" {
+ return fmt.Errorf("%s: missing zone", opts)
}
path = fmt.Sprintf("/application/v4/tenant/%s/application/%s/instance/%s/deploy/%s-%s",
- deployment.Application.Tenant,
- deployment.Application.Application,
- deployment.Application.Instance,
- deployment.Zone.Environment,
- deployment.Zone.Region)
+ opts.Deployment.Application.Tenant,
+ opts.Deployment.Application.Application,
+ opts.Deployment.Application.Instance,
+ opts.Deployment.Zone.Environment,
+ opts.Deployment.Zone.Region)
}
- u, err := url.Parse(deployment.TargetURL + path)
+ u, err := opts.url(path)
if err != nil {
return err
}
- _, err = uploadApplicationPackage(u, deployment)
+ _, err = uploadApplicationPackage(u, opts)
return err
}
-func uploadApplicationPackage(url *url.URL, deployment DeploymentOpts) (int64, error) {
- zipReader, err := deployment.ApplicationPackage.zipReader()
+func uploadApplicationPackage(url *url.URL, opts DeploymentOpts) (int64, error) {
+ zipReader, err := opts.ApplicationPackage.zipReader()
if err != nil {
return 0, err
}
@@ -220,8 +235,8 @@ func uploadApplicationPackage(url *url.URL, deployment DeploymentOpts) (int64, e
Header: header,
Body: io.NopCloser(zipReader),
}
- if deployment.APIKey != nil {
- signer := NewRequestSigner(deployment.Application.SerializedForm(), deployment.APIKey)
+ if opts.APIKey != nil {
+ signer := NewRequestSigner(opts.Deployment.Application.SerializedForm(), opts.APIKey)
if err := signer.SignRequest(request); err != nil {
return 0, err
}
@@ -236,15 +251,14 @@ func uploadApplicationPackage(url *url.URL, deployment DeploymentOpts) (int64, e
var sessionResponse struct {
SessionID string `json:"session-id"`
}
+ sessionResponse.SessionID = "0" // Set a default session ID for responses that don't contain int (e.g. cloud deployment)
if response.StatusCode/100 == 4 {
return 0, fmt.Errorf("Invalid application package (%s)\n\n%s", response.Status, extractError(response.Body))
} else if response.StatusCode != 200 {
return 0, fmt.Errorf("Error from %s at %s (%s):\n%s", strings.ToLower(serviceDescription), request.URL.Host, response.Status, util.ReaderToJSON(response.Body))
} else {
jsonDec := json.NewDecoder(response.Body)
- if err := jsonDec.Decode(&sessionResponse); err != nil {
- sessionResponse.SessionID = "0" // No JSON in response
- }
+ jsonDec.Decode(&sessionResponse) // Ignore error in case this is a non-JSON response
}
return strconv.ParseInt(sessionResponse.SessionID, 10, 64)
}