aboutsummaryrefslogtreecommitdiffstats
path: root/client/go/cmd
diff options
context:
space:
mode:
authorArne Juul <arnej@yahooinc.com>2022-08-21 19:54:05 +0000
committerArne Juul <arnej@yahooinc.com>2022-08-24 15:37:32 +0000
commitba290bf93310ca1f2273c11f3f877231a850a2cc (patch)
treeefbd97105725d5b2163d4869b7742106ac0c8cfc /client/go/cmd
parentc8faaace7d59d9aec058e134ea0c24d202134817 (diff)
add go code for vespa-deploy
* using same logic as perl script as far as possible * uses vespa-curl-wrapper for HTTP with TLS
Diffstat (limited to 'client/go/cmd')
-rw-r--r--client/go/cmd/deploy/activate.go44
-rw-r--r--client/go/cmd/deploy/curl.go114
-rw-r--r--client/go/cmd/deploy/fetch.go94
-rw-r--r--client/go/cmd/deploy/options.go37
-rw-r--r--client/go/cmd/deploy/persist.go105
-rw-r--r--client/go/cmd/deploy/prepare.go80
-rw-r--r--client/go/cmd/deploy/results.go86
-rw-r--r--client/go/cmd/deploy/upload.go125
-rw-r--r--client/go/cmd/deploy/urls.go69
-rw-r--r--client/go/cmd/logfmt/runlogfmt.go12
10 files changed, 757 insertions, 9 deletions
diff --git a/client/go/cmd/deploy/activate.go b/client/go/cmd/deploy/activate.go
new file mode 100644
index 00000000000..a6b7de81342
--- /dev/null
+++ b/client/go/cmd/deploy/activate.go
@@ -0,0 +1,44 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// main entry point for vespa-deploy activate
+
+func RunActivate(opts *Options, args []string) error {
+ var sessId string
+ if len(args) == 0 {
+ sessId = getSessionIdFromFile(opts.Tenant)
+ } else {
+ sessId = args[0]
+ }
+ src := makeConfigsourceUrl(opts)
+ url := src + pathPrefix(opts) + "/" + sessId + "/active"
+ url = addUrlPropertyFromFlag(url, opts.Verbose, "verbose")
+ url = addUrlPropertyFromOption(url, strconv.Itoa(opts.Timeout), "timeout")
+ fmt.Printf("Activating session %s using %s\n", sessId, urlWithoutQuery(url))
+ output, err := curlPut(url, src)
+ if err != nil {
+ return err
+ }
+ var result ActivateResult
+ code, err := decodeResponse(output, &result)
+ if err != nil {
+ return err
+ }
+ if code == 200 {
+ fmt.Println(result.Message)
+ fmt.Println("Checksum: ", result.Application.Checksum)
+ fmt.Println("Timestamp: ", result.Deploy.Timestamp)
+ fmt.Println("Generation:", result.Application.Generation)
+ } else {
+ err = fmt.Errorf("Request failed. HTTP status code: %d\n%s", code, result.Message)
+ }
+ return err
+}
diff --git a/client/go/cmd/deploy/curl.go b/client/go/cmd/deploy/curl.go
new file mode 100644
index 00000000000..67edb0ee010
--- /dev/null
+++ b/client/go/cmd/deploy/curl.go
@@ -0,0 +1,114 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "strings"
+
+ "github.com/vespa-engine/vespa/client/go/vespa"
+)
+
+func curlPut(url string, cfgSrc string) (string, error) {
+ args := append(curlPutArgs(), url)
+ return runCurl(args, new(strings.Reader), cfgSrc)
+}
+
+func curlPost(url string, input io.Reader, cfgSrc string) (string, error) {
+ args := append(curlPostArgs(), url)
+ return runCurl(args, input, cfgSrc)
+}
+
+func curlPostZip(url string, input io.Reader, cfgSrc string) (string, error) {
+ args := append(curlPostZipArgs(), url)
+ return runCurl(args, input, cfgSrc)
+}
+
+func curlGet(url string, output io.Writer) error {
+ args := append(curlGetArgs(), url)
+ cmd := exec.Command(curlWrapper(), args...)
+ cmd.Stdout = output
+ cmd.Stderr = os.Stderr
+ // fmt.Printf("running command: %v\n", cmd)
+ err := cmd.Run()
+ return err
+}
+
+func urlWithoutQuery(url string) string {
+ before, _, _ := strings.Cut(url, "?")
+ return before
+}
+
+func getOutputFromCmd(program string, args ...string) (string, error) {
+ cmd := exec.Command(program, args...)
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ return out.String(), err
+}
+
+func runCurl(args []string, input io.Reader, cfgSrc string) (string, error) {
+ cmd := exec.Command(curlWrapper(), args...)
+ cmd.Stdin = input
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = os.Stderr
+ // fmt.Printf("running command: %v\n", cmd)
+ err := cmd.Run()
+ // fmt.Printf("output: %s\n", out.String())
+ if err != nil {
+ if cmd.ProcessState.ExitCode() == 7 {
+ return "", fmt.Errorf("HTTP request failed. Could not connect to %s", cfgSrc)
+ }
+ return "", fmt.Errorf("HTTP request failed with curl %s", err.Error())
+ }
+ return out.String(), err
+}
+
+func curlWrapper() string {
+ return vespa.FindHome() + "/libexec/vespa/vespa-curl-wrapper"
+}
+
+func commonCurlArgs() []string {
+ return []string{
+ "-A", "vespa-deploy",
+ "--silent",
+ "--show-error",
+ "--connect-timeout", "30",
+ "--max-time", "1200",
+ }
+}
+
+func curlPutArgs() []string {
+ return append(commonCurlArgs(),
+ "--write-out", "%{http_code}",
+ "--request", "PUT")
+}
+
+func curlGetArgs() []string {
+ return append(commonCurlArgs(),
+ "--request", "GET")
+}
+
+func curlPostArgs() []string {
+ return append(commonCurlArgs(),
+ "--write-out", "%{http_code}",
+ "--request", "POST",
+ "--header", "Content-Type: application/x-gzip",
+ "--data-binary", "@-")
+}
+
+func curlPostZipArgs() []string {
+ return append(commonCurlArgs(),
+ "--write-out", "%{http_code}",
+ "--request", "POST",
+ "--header", "Content-Type: application/zip",
+ "--data-binary", "@-")
+}
diff --git a/client/go/cmd/deploy/fetch.go b/client/go/cmd/deploy/fetch.go
new file mode 100644
index 00000000000..3b65509c0a3
--- /dev/null
+++ b/client/go/cmd/deploy/fetch.go
@@ -0,0 +1,94 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// main entry point for vespa-deploy fetch
+
+func RunFetch(opts *Options, args []string) error {
+ dirName := "."
+ if len(args) > 0 {
+ dirName = args[0]
+ }
+ src := makeConfigsourceUrl(opts)
+ url := src +
+ "/application/v2" +
+ "/tenant/" + opts.Tenant +
+ "/application/" + opts.Application +
+ "/environment/" + opts.Environment +
+ "/region/" + opts.Region +
+ "/instance/" + opts.Instance +
+ "/content/"
+
+ url = addUrlPropertyFromOption(url, strconv.Itoa(opts.Timeout), "timeout")
+ fmt.Printf("Writing active application to %s\n(using %s)\n", dirName, urlWithoutQuery(url))
+ var out bytes.Buffer
+ err := curlGet(url, &out)
+ if err != nil {
+ return err
+ }
+ fetchDirectory(dirName, &out)
+ return err
+}
+
+func fetchDirectory(name string, input *bytes.Buffer) {
+ err := os.MkdirAll(name, 0755)
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ codec := json.NewDecoder(input)
+ var result []string
+ err = codec.Decode(&result)
+ if err != nil {
+ fmt.Printf("ERROR: %v [%v] <<< %s\n", result, err, input.String())
+ return
+ }
+ for _, entry := range result {
+ fmt.Println("GET", entry)
+ fn := name + "/" + getPartAfterSlash(entry)
+ if strings.HasSuffix(entry, "/") {
+ var out bytes.Buffer
+ err := curlGet(entry, &out)
+ if err != nil {
+ fmt.Println("FAILED", err)
+ return
+ }
+ fetchDirectory(fn, &out)
+ } else {
+ f, err := os.Create(fn)
+ if err != nil {
+ fmt.Println("FAILED", err)
+ return
+ }
+ defer f.Close()
+ err = curlGet(entry, f)
+ if err != nil {
+ fmt.Println("FAILED", err)
+ return
+ }
+ }
+ }
+}
+
+func getPartAfterSlash(path string) string {
+ parts := strings.Split(path, "/")
+ idx := len(parts) - 1
+ if idx > 1 && parts[idx] == "" {
+ return parts[idx-1]
+ }
+ if idx > 0 {
+ return parts[idx]
+ }
+ panic("cannot find part after slash: " + path)
+}
diff --git a/client/go/cmd/deploy/options.go b/client/go/cmd/deploy/options.go
new file mode 100644
index 00000000000..2f71f779044
--- /dev/null
+++ b/client/go/cmd/deploy/options.go
@@ -0,0 +1,37 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+type CmdType int
+
+const (
+ CmdNone CmdType = iota
+ CmdUpload
+ CmdPrepare
+ CmdActivate
+ CmdFetch
+)
+
+type Options struct {
+ Command CmdType
+
+ Verbose bool
+ DryRun bool
+ Force bool
+ Hosted bool
+
+ Application string
+ Environment string
+ From string
+ Instance string
+ Region string
+ Rotations string
+ ServerHost string
+ Tenant string
+ VespaVersion string
+
+ Timeout int
+ PortNumber int
+}
diff --git a/client/go/cmd/deploy/persist.go b/client/go/cmd/deploy/persist.go
new file mode 100644
index 00000000000..78fe063ea0e
--- /dev/null
+++ b/client/go/cmd/deploy/persist.go
@@ -0,0 +1,105 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ // "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+const (
+ cloudconfigDir = ".cloudconfig"
+ configsourceUrlUsedFileName = "deploy-configsource-url-used"
+ sessionIdFileName = "deploy-session-id"
+)
+
+func createCloudconfigDir() (string, error) {
+ userHome, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+ home := filepath.Join(userHome, cloudconfigDir)
+ if err := os.MkdirAll(home, 0700); err != nil {
+ return "", err
+ }
+ return home, nil
+}
+
+func configsourceUrlUsedFile() string {
+ home, err := createCloudconfigDir()
+ if err != nil {
+ home = "/tmp"
+ }
+ return filepath.Join(home, configsourceUrlUsedFileName)
+}
+
+func createTenantDir(tenant string) string {
+ home, err := createCloudconfigDir()
+ if err != nil {
+ panic(err)
+ }
+ tdir := filepath.Join(home, tenant)
+ if err := os.MkdirAll(tdir, 0700); err != nil {
+ panic(err)
+ }
+ return tdir
+}
+
+func writeConfigsourceUrlUsed(url string) {
+ fn := configsourceUrlUsedFile()
+ os.WriteFile(fn, []byte(url), 0600)
+}
+
+func getConfigsourceUrlUsed() string {
+ fn := configsourceUrlUsedFile()
+ bytes, err := os.ReadFile(fn)
+ if err != nil {
+ return ""
+ }
+ return string(bytes)
+}
+
+func writeSessionIdFromResponseToFile(tenant, response string) {
+ newSessionId := getSessionIdFromResponse(response)
+ writeSessionIdToFile(tenant, newSessionId)
+}
+
+func writeSessionIdToFile(tenant, newSessionId string) {
+ if newSessionId != "" {
+ dir := createTenantDir(tenant)
+ fn := filepath.Join(dir, sessionIdFileName)
+ os.WriteFile(fn, []byte(newSessionId), 0600)
+ // fmt.Printf("wrote %s to %s\n", newSessionId, fn)
+ }
+}
+
+func getSessionIdFromResponse(response string) string {
+ _, after, found := strings.Cut(response, `"session-id":"`)
+ if !found {
+ return ""
+ }
+ digits, _, found := strings.Cut(after, `"`)
+ if !found {
+ return ""
+ }
+ if _, err := strconv.Atoi(digits); err != nil {
+ return ""
+ }
+ return digits
+}
+
+func getSessionIdFromFile(tenant string) string {
+ dir := createTenantDir(tenant)
+ fn := filepath.Join(dir, sessionIdFileName)
+ bytes, err := os.ReadFile(fn)
+ if err == nil {
+ // fmt.Printf("Session-id '%s' found from file %s\n", string(bytes), fn)
+ return string(bytes)
+ }
+ panic("Could not read session id from file, and no session id supplied as argument. Exiting.")
+}
diff --git a/client/go/cmd/deploy/prepare.go b/client/go/cmd/deploy/prepare.go
new file mode 100644
index 00000000000..ace14c9b949
--- /dev/null
+++ b/client/go/cmd/deploy/prepare.go
@@ -0,0 +1,80 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+)
+
+// main entry point for vespa-deploy prepare
+
+func looksLikeNumber(s string) bool {
+ var i, j int
+ n, err := fmt.Sscanf(s+" 123", "%d %d", &i, &j)
+ return n == 2 && err == nil
+}
+
+func RunPrepare(opts *Options, args []string) (err error) {
+ var response string
+ if len(args) == 0 {
+ // prepare last upload
+ sessId := getSessionIdFromFile(opts.Tenant)
+ response, err = doPrepare(opts, sessId)
+ } else if isFileOrDir(args[0]) {
+ RunUpload(opts, args)
+ return RunPrepare(opts, []string{})
+ } else if looksLikeNumber(args[0]) {
+ response, err = doPrepare(opts, args[0])
+ } else {
+ err = fmt.Errorf("Command failed. No directory or zip file found: '%s'", args[0])
+ }
+ if err != nil {
+ return err
+ }
+ var result PrepareResult
+ code, err := decodeResponse(response, &result)
+ if err != nil {
+ return err
+ }
+ for _, entry := range result.Log {
+ fmt.Println(entry.Level+":", entry.Message)
+ }
+ if code != 200 {
+ return fmt.Errorf("Request failed. HTTP status code: %d\n%s", code, result.Message)
+ }
+ fmt.Println(result.Message)
+ return err
+}
+
+func isFileOrDir(name string) bool {
+ f, err := os.Open(name)
+ if err != nil {
+ return false
+ }
+ st, err := f.Stat()
+ if err != nil {
+ return false
+ }
+ return st.Mode().IsRegular() || st.Mode().IsDir()
+}
+
+func doPrepare(opts *Options, sessionId string) (output string, err error) {
+ src := makeConfigsourceUrl(opts)
+ url := src + pathPrefix(opts) + "/" + sessionId + "/prepared"
+ url = addUrlPropertyFromFlag(url, opts.Force, "ignoreValidationErrors")
+ url = addUrlPropertyFromFlag(url, opts.DryRun, "dryRun")
+ url = addUrlPropertyFromFlag(url, opts.Verbose, "verbose")
+ url = addUrlPropertyFromFlag(url, opts.Hosted, "hostedVespa")
+ url = addUrlPropertyFromOption(url, opts.Application, "applicationName")
+ url = addUrlPropertyFromOption(url, opts.Instance, "instance")
+ url = addUrlPropertyFromOption(url, strconv.Itoa(opts.Timeout), "timeout")
+ url = addUrlPropertyFromOption(url, opts.Rotations, "rotations")
+ url = addUrlPropertyFromOption(url, opts.VespaVersion, "vespaVersion")
+ fmt.Printf("Preparing session %s using %s\n", sessionId, urlWithoutQuery(url))
+ output, err = curlPut(url, src)
+ return
+}
diff --git a/client/go/cmd/deploy/results.go b/client/go/cmd/deploy/results.go
new file mode 100644
index 00000000000..47a05e45ab7
--- /dev/null
+++ b/client/go/cmd/deploy/results.go
@@ -0,0 +1,86 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "encoding/json"
+ "strings"
+)
+
+func decodeResponse(response string, v interface{}) (code int, err error) {
+ codec := json.NewDecoder(strings.NewReader(response))
+ err = codec.Decode(v)
+ if err != nil {
+ return
+ }
+ err = codec.Decode(&code)
+ return
+}
+
+type UploadResult struct {
+ Log []struct {
+ Time int64 `json:"time"`
+ Level string `json:"level"`
+ Message string `json:"message"`
+ ApplicationPackage bool `json:"applicationPackage"`
+ } `json:"log"`
+ Tenant string `json:"tenant"`
+ SessionID string `json:"session-id"`
+ Prepared string `json:"prepared"`
+ Content string `json:"content"`
+ Message string `json:"message"`
+ ErrorCode string `json:"error-code"`
+}
+
+type PrepareResult struct {
+ Log []struct {
+ Time int64 `json:"time"`
+ Level string `json:"level"`
+ Message string `json:"message"`
+ ApplicationPackage bool `json:"applicationPackage"`
+ } `json:"log"`
+ Tenant string `json:"tenant"`
+ SessionID string `json:"session-id"`
+ Activate string `json:"activate"`
+ Message string `json:"message"`
+ ErrorCode string `json:"error-code"`
+ /* not used at the moment:
+ ConfigChangeActions struct {
+ Restart []struct {
+ ClusterName string `json:"clusterName"`
+ ClusterType string `json:"clusterType"`
+ ServiceType string `json:"serviceType"`
+ Messages []string `json:"messages"`
+ Services []struct {
+ ServiceName string `json:"serviceName"`
+ ServiceType string `json:"serviceType"`
+ ConfigID string `json:"configId"`
+ HostName string `json:"hostName"`
+ } `json:"services"`
+ } `json:"restart"`
+ Refeed []interface{} `json:"refeed"`
+ Reindex []interface{} `json:"reindex"`
+ } `json:"configChangeActions"`
+ */
+}
+
+type ActivateResult struct {
+ Deploy struct {
+ From string `json:"from"`
+ Timestamp int64 `json:"timestamp"`
+ InternalRedeploy bool `json:"internalRedeploy"`
+ } `json:"deploy"`
+ Application struct {
+ ID string `json:"id"`
+ Checksum string `json:"checksum"`
+ Generation int `json:"generation"`
+ PreviousActiveGeneration int `json:"previousActiveGeneration"`
+ } `json:"application"`
+ Tenant string `json:"tenant"`
+ SessionID string `json:"session-id"`
+ Message string `json:"message"`
+ URL string `json:"url"`
+ ErrorCode string `json:"error-code"`
+}
diff --git a/client/go/cmd/deploy/upload.go b/client/go/cmd/deploy/upload.go
new file mode 100644
index 00000000000..57d84e9923c
--- /dev/null
+++ b/client/go/cmd/deploy/upload.go
@@ -0,0 +1,125 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// main entry point for vespa-deploy upload
+
+func RunUpload(opts *Options, args []string) error {
+ output, err := doUpload(opts, args)
+ if err != nil {
+ return err
+ }
+ var result UploadResult
+ code, err := decodeResponse(output, &result)
+ if err != nil {
+ return err
+ }
+ if code != 200 {
+ return fmt.Errorf("Request failed. HTTP status code: %d\n%s", code, result.Message)
+ }
+ fmt.Println(result.Message)
+ writeSessionIdToFile(opts.Tenant, result.SessionID)
+ return nil
+}
+
+func doUpload(opts *Options, args []string) (result string, err error) {
+ sources := makeConfigsourceUrls(opts)
+ for idx, src := range sources {
+ if idx > 0 {
+ fmt.Println(err)
+ fmt.Println("Retrying with another config server")
+ }
+ result, err = uploadToConfigSource(opts, src, args)
+ if err == nil {
+ writeConfigsourceUrlUsed(src)
+ return
+ }
+ }
+ return
+}
+
+func uploadToConfigSource(opts *Options, src string, args []string) (string, error) {
+ if opts.From != "" {
+ return uploadFrom(opts, src)
+ }
+ if len(args) == 0 {
+ return uploadDirectory(opts, src, ".")
+ } else {
+ f, err := os.Open(args[0])
+ if err != nil {
+ return "", fmt.Errorf("Command failed. No such directory found: '%s'", args[0])
+ }
+ defer f.Close()
+ st, err := f.Stat()
+ if err != nil {
+ return "", err
+ }
+ if st.Mode().IsRegular() {
+ if !strings.HasSuffix(args[0], ".zip") {
+ return "", fmt.Errorf("Application must be a zip file, was '%s'", args[0])
+ }
+ return uploadFile(opts, src, f, args[0])
+ }
+ if st.Mode().IsDir() {
+ return uploadDirectory(opts, src, args[0])
+ }
+ return "", fmt.Errorf("Bad arg '%s' with FileMode %v", args[0], st.Mode())
+ }
+}
+
+func uploadFrom(opts *Options, src string) (string, error) {
+ url := src + pathPrefix(opts)
+ url = addUrlPropertyFromOption(url, opts.From, "from")
+ url = addUrlPropertyFromFlag(url, opts.Verbose, "verbose")
+ // disallowed by system test:
+ // fmt.Printf("Upload from URL %s using %s\n", opts.From, urlWithoutQuery(url))
+ output, err := curlPost(url, nil, src)
+ return output, err
+}
+
+func uploadFile(opts *Options, src string, f *os.File, fileName string) (string, error) {
+ url := src + pathPrefix(opts)
+ url = addUrlPropertyFromFlag(url, opts.Verbose, "verbose")
+ fmt.Printf("Uploading application '%s' using %s\n", fileName, urlWithoutQuery(url))
+ output, err := curlPostZip(url, f, src)
+ return output, err
+}
+
+func uploadDirectory(opts *Options, src string, dirName string) (string, error) {
+ url := src + pathPrefix(opts)
+ url = addUrlPropertyFromFlag(url, opts.Verbose, "verbose")
+ fmt.Printf("Uploading application '%s' using %s\n", dirName, urlWithoutQuery(url))
+ tarCmd := tarCommand(dirName)
+ pipe, err := tarCmd.StdoutPipe()
+ if err != nil {
+ return "", err
+ }
+ err = tarCmd.Start()
+ if err != nil {
+ return "", err
+ }
+ output, err := curlPost(url, pipe, src)
+ tarCmd.Wait()
+ return output, err
+}
+
+func tarCommand(dirName string) *exec.Cmd {
+ args := []string{
+ "-C", dirName,
+ "--dereference",
+ "--exclude=.[a-zA-Z0-9]*",
+ "--exclude=ext",
+ "-czf", "-",
+ ".",
+ }
+ return exec.Command("tar", args...)
+}
diff --git a/client/go/cmd/deploy/urls.go b/client/go/cmd/deploy/urls.go
new file mode 100644
index 00000000000..a865006df0d
--- /dev/null
+++ b/client/go/cmd/deploy/urls.go
@@ -0,0 +1,69 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa-deploy command
+// Author: arnej
+
+package deploy
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/vespa-engine/vespa/client/go/vespa"
+)
+
+func makeConfigsourceUrl(opts *Options) string {
+ src := makeConfigsourceUrls(opts)[0]
+ if opts.Command == CmdPrepare || opts.Command == CmdActivate {
+ if lastUsed := getConfigsourceUrlUsed(); lastUsed != "" {
+ return lastUsed
+ }
+ fmt.Printf("Could not read config server URL used for previous upload of an application package, trying to use %s\n", src)
+ }
+ return src
+}
+
+func makeConfigsourceUrls(opts *Options) []string {
+ var results = make([]string, 0, 3)
+ if opts.ServerHost == "" {
+ home := vespa.FindHome()
+ configsources, _ := getOutputFromCmd(home+"/bin/vespa-print-default", "configservers_http")
+ for _, src := range strings.Split(configsources, "\n") {
+ colonParts := strings.Split(src, ":")
+ if len(colonParts) > 1 {
+ // XXX overwrites port number from above - is this sensible?
+ src = fmt.Sprintf("%s:%s:%d", colonParts[0], colonParts[1], opts.PortNumber)
+ results = append(results, src)
+ }
+ }
+ if len(results) == 0 {
+ fmt.Println("Could not get url to config server, make sure that VESPA_CONFIGSERVERS is set")
+ results = append(results, fmt.Sprintf("http://localhost:%d", opts.PortNumber))
+ }
+ } else {
+ results = append(results, fmt.Sprintf("http://%s:%d", opts.ServerHost, opts.PortNumber))
+ }
+ return results
+}
+
+func pathPrefix(opts *Options) string {
+ return "/application/v2/tenant/" + opts.Tenant + "/session"
+}
+
+func addUrlPropertyFromFlag(url string, flag bool, propName string) string {
+ if !flag {
+ return url
+ } else {
+ return addUrlPropertyFromOption(url, "true", propName)
+ }
+}
+
+func addUrlPropertyFromOption(url, flag, propName string) string {
+ if flag == "" {
+ return url
+ }
+ if strings.Contains(url, "?") {
+ return url + "&" + propName + "=" + flag
+ } else {
+ return url + "?" + propName + "=" + flag
+ }
+}
diff --git a/client/go/cmd/logfmt/runlogfmt.go b/client/go/cmd/logfmt/runlogfmt.go
index 5abc4cc1cb8..5b9a3ac0870 100644
--- a/client/go/cmd/logfmt/runlogfmt.go
+++ b/client/go/cmd/logfmt/runlogfmt.go
@@ -8,6 +8,8 @@ import (
"bufio"
"fmt"
"os"
+
+ "github.com/vespa-engine/vespa/client/go/vespa"
)
func inputIsPipe() bool {
@@ -22,20 +24,12 @@ func inputIsPipe() bool {
}
}
-func vespaHome() string {
- ev := os.Getenv("VESPA_HOME")
- if ev == "" {
- return "/opt/vespa"
- }
- return ev
-}
-
// main entry point for vespa-logfmt
func RunLogfmt(opts *Options, args []string) {
if len(args) == 0 {
if !inputIsPipe() {
- args = append(args, vespaHome()+"/logs/vespa/vespa.log")
+ args = append(args, vespa.FindHome()+"/logs/vespa/vespa.log")
} else {
formatFile(opts, os.Stdin)
}