Bootaction client initial implementation
This is the implementation of a standalone bootaction signal API client to be used on deployed nodes for signaling details and results of bootactions. Change-Id: Icc3d39253a02457a76f79d7a7c06333ae494d735
This commit is contained in:
parent
9a52dca199
commit
ecb3e01527
11
Makefile
11
Makefile
@ -24,7 +24,7 @@ USE_PROXY ?= false
|
||||
PUSH_IMAGE ?= false
|
||||
LABEL ?= commit-id
|
||||
IMAGE ?= ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${IMAGE_NAME}:${IMAGE_TAG}
|
||||
GO_BUILDER ?= docker.io/golang:1.10-alpine
|
||||
GO_BUILDER ?= docker.io/golang:1.10-stretch
|
||||
|
||||
export
|
||||
|
||||
@ -38,7 +38,7 @@ run_images: run_drydock
|
||||
|
||||
# Run tests
|
||||
.PHONY: tests
|
||||
tests: pep8 security docs unit_tests
|
||||
tests: pep8 security docs unit_tests test_baclient
|
||||
|
||||
# Install external (not managed by tox/pip) dependencies
|
||||
external_dep: requirements-host.txt requirements-host-test.txt
|
||||
@ -112,7 +112,12 @@ endif
|
||||
# Make target for building bootaction signal client
|
||||
.PHONY: build_baclient
|
||||
build_baclient: external_dep
|
||||
docker run -tv $(shell realpath go):/work -v $(shell realpath $(BUILD_DIR)):/build -e GOPATH=/work $(GO_BUILDER) go build -o /build/baclient baclient
|
||||
docker run -tv $(shell realpath go):/work -v $(shell realpath $(BUILD_DIR)):/build -e GOPATH=/work $(GO_BUILDER) go build -o /build/baclient baclient
|
||||
|
||||
# Make target for testing bootaction signal client
|
||||
.PHONY: test_baclient
|
||||
test_baclient: external_dep
|
||||
docker run -tv $(shell realpath go):/work -e GOPATH=/work $(GO_BUILDER) go test -v baclient
|
||||
|
||||
.PHONY: docs
|
||||
docs: clean drydock_docs
|
||||
|
71
go/src/baclient/api_client.go
Normal file
71
go/src/baclient/api_client.go
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Interact with the Drydock Bootaction API
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (msg *BootactionMessage) post(url string, key string) error {
|
||||
timeout, _ := time.ParseDuration("60s")
|
||||
|
||||
api_request, err := buildRequest(url, key, msg)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error build API request: %s", err)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
resp, err := client.Do(api_request)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending the API request: %s\n", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return fmt.Errorf("Error response: %s", resp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildRequest(url string, key string, msg *BootactionMessage) (*http.Request, error) {
|
||||
body, err := json.Marshal(msg)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error encoding message: %s\n", err)
|
||||
}
|
||||
|
||||
bodyReader := bytes.NewBuffer(body)
|
||||
req, err := http.NewRequest(http.MethodPost, url, bodyReader)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating API request: %s\n", err)
|
||||
}
|
||||
|
||||
req.Header.Add("X-Bootaction-Key", key)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
return req, nil
|
||||
}
|
@ -1,9 +1,131 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a CLI for interacting with the Airship-Drydock Bootaction Signal
|
||||
// API
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello World!\n")
|
||||
conf := parseConfig()
|
||||
|
||||
// Indicates the help CLI flag was given
|
||||
if conf == nil {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !conf.validate() {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if conf.wrapExecutable != "" {
|
||||
err = reportExecution(conf)
|
||||
} else {
|
||||
err = reportMessage(conf)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Bootaction status posted.\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func reportExecution(conf *ClientConfig) error {
|
||||
url := renderURL(conf)
|
||||
|
||||
msg, _ := newMessage("Bootaction starting execution.", false, "")
|
||||
err := msg.post(url, conf.bootactionKey)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error accessing API: %s", err)
|
||||
}
|
||||
|
||||
result := executeAction(conf.wrapExecutable, conf.proxyEnvironment)
|
||||
|
||||
if result {
|
||||
msg, _ = newMessage("Bootaction execution successful.", false, SUCCESS)
|
||||
} else {
|
||||
msg, _ = newMessage("Bootaction execution failed.", true, FAILURE)
|
||||
}
|
||||
|
||||
err = msg.post(url, conf.bootactionKey)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error accessing API: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func reportMessage(conf *ClientConfig) error {
|
||||
url := renderURL(conf)
|
||||
|
||||
msg, err := newMessage(conf.message, conf.isError, conf.status)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating message: %s\n", err)
|
||||
}
|
||||
|
||||
err = msg.post(url, conf.bootactionKey)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error accesing API: %s\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderURL(conf *ClientConfig) (fullURL string) {
|
||||
fullURL = fmt.Sprintf("%s/%s/", conf.apiURL, conf.bootactionID)
|
||||
return
|
||||
}
|
||||
|
||||
func newMessageDetail(msg string, isError bool) (*BootactionDetail, error) {
|
||||
// isError defaults to false if nil
|
||||
if msg == "" {
|
||||
return nil, fmt.Errorf("Error creating MessageDetail, message string undefined.")
|
||||
}
|
||||
|
||||
var msg_detail BootactionDetail
|
||||
|
||||
msg_detail.Message = msg
|
||||
msg_detail.IsError = isError
|
||||
|
||||
return &msg_detail, nil
|
||||
}
|
||||
|
||||
func newMessage(msg string, isError bool, finalStatus string) (*BootactionMessage, error) {
|
||||
msg_detail, err := newMessageDetail(msg, isError)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating Message: %s", err)
|
||||
}
|
||||
|
||||
var message BootactionMessage
|
||||
|
||||
message.Status = finalStatus
|
||||
message.Details = []BootactionDetail{*msg_detail}
|
||||
|
||||
return &message, nil
|
||||
}
|
||||
|
232
go/src/baclient/baclient_test.go
Normal file
232
go/src/baclient/baclient_test.go
Normal file
@ -0,0 +1,232 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Tests for the baclient
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
mrand "math/rand"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPostIncompleteMessage(t *testing.T) {
|
||||
var conf ClientConfig
|
||||
|
||||
bootactionID := generateID()
|
||||
bootactionKey := generateKey()
|
||||
|
||||
ts := buildTestServer(t, bootactionID, bootactionKey, "")
|
||||
defer ts.Close()
|
||||
|
||||
conf.apiURL = fmt.Sprintf("%s/api/v1.0/bootaction", ts.URL)
|
||||
conf.bootactionID = bootactionID
|
||||
conf.bootactionKey = bootactionKey
|
||||
conf.message = "Testing 1 2 3"
|
||||
conf.isError = false
|
||||
|
||||
err := reportMessage(&conf)
|
||||
|
||||
if err != nil {
|
||||
t.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPostSuccessMessage(t *testing.T) {
|
||||
var conf ClientConfig
|
||||
|
||||
bootactionID := generateID()
|
||||
bootactionKey := generateKey()
|
||||
|
||||
ts := buildTestServer(t, bootactionID, bootactionKey, SUCCESS)
|
||||
defer ts.Close()
|
||||
|
||||
conf.apiURL = fmt.Sprintf("%s/api/v1.0/bootaction", ts.URL)
|
||||
conf.bootactionID = bootactionID
|
||||
conf.bootactionKey = bootactionKey
|
||||
conf.message = "Testing 1 2 3"
|
||||
conf.isError = false
|
||||
conf.status = SUCCESS
|
||||
|
||||
err := reportMessage(&conf)
|
||||
|
||||
if err != nil {
|
||||
t.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostFailureMessage(t *testing.T) {
|
||||
var conf ClientConfig
|
||||
|
||||
bootactionID := generateID()
|
||||
bootactionKey := generateKey()
|
||||
|
||||
ts := buildTestServer(t, bootactionID, bootactionKey, FAILURE)
|
||||
defer ts.Close()
|
||||
|
||||
conf.apiURL = fmt.Sprintf("%s/api/v1.0/bootaction", ts.URL)
|
||||
conf.bootactionID = bootactionID
|
||||
conf.bootactionKey = bootactionKey
|
||||
conf.message = "Testing 1 2 3"
|
||||
conf.isError = true
|
||||
conf.status = FAILURE
|
||||
|
||||
err := reportMessage(&conf)
|
||||
|
||||
if err != nil {
|
||||
t.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostSuccessExec(t *testing.T) {
|
||||
var conf ClientConfig
|
||||
|
||||
bootactionID := generateID()
|
||||
bootactionKey := generateKey()
|
||||
|
||||
ts := buildTestServer(t, bootactionID, bootactionKey, SUCCESS)
|
||||
defer ts.Close()
|
||||
|
||||
conf.apiURL = fmt.Sprintf("%s/api/v1.0/bootaction", ts.URL)
|
||||
conf.bootactionID = bootactionID
|
||||
conf.bootactionKey = bootactionKey
|
||||
conf.wrapExecutable = "/bin/true"
|
||||
|
||||
err := reportExecution(&conf)
|
||||
|
||||
if err != nil {
|
||||
t.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostFailureExec(t *testing.T) {
|
||||
var conf ClientConfig
|
||||
|
||||
bootactionID := generateID()
|
||||
bootactionKey := generateKey()
|
||||
|
||||
ts := buildTestServer(t, bootactionID, bootactionKey, FAILURE)
|
||||
defer ts.Close()
|
||||
|
||||
conf.apiURL = fmt.Sprintf("%s/api/v1.0/bootaction", ts.URL)
|
||||
conf.bootactionID = bootactionID
|
||||
conf.bootactionKey = bootactionKey
|
||||
conf.wrapExecutable = "/bin/false"
|
||||
|
||||
err := reportExecution(&conf)
|
||||
|
||||
if err != nil {
|
||||
t.Error(fmt.Sprintf("%s", err))
|
||||
}
|
||||
}
|
||||
|
||||
func generateID() string {
|
||||
// In order to stay within the Go stdlib and because real randomness here
|
||||
// isn't that valuable, just pick one of a few hardcoded ulids
|
||||
var ulidPool [5]string = [5]string{
|
||||
"01CP38QN33KZ5E2MZBC0S7PJHR",
|
||||
"01CP393Q44NW9TFVT1W8QTY2PP",
|
||||
"01CP39489G7SRNJX6G1E61P4X5",
|
||||
"01CP394JQEEH6127FCQVB4TBKY",
|
||||
"01CP394TFYMH38VSM4JNJZHM9Y",
|
||||
}
|
||||
|
||||
selector := mrand.Int31n(5)
|
||||
return ulidPool[selector]
|
||||
}
|
||||
|
||||
func generateKey() string {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
||||
keyHex := make([]byte, hex.EncodedLen(len(key)))
|
||||
hex.Encode(keyHex, key)
|
||||
|
||||
return string(keyHex)
|
||||
}
|
||||
|
||||
func buildTestServer(t *testing.T, bootactionID string, bootactionKey string, expectedResult string) *httptest.Server {
|
||||
hf := func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
t.Logf("Request used method %s.\n", r.Method)
|
||||
w.WriteHeader(405)
|
||||
return
|
||||
}
|
||||
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
||||
if contentType != "application/json" {
|
||||
t.Logf("Request had content type '%s'\n", contentType)
|
||||
w.WriteHeader(415)
|
||||
return
|
||||
}
|
||||
|
||||
reqKey := r.Header.Get("X-Bootaction-Key")
|
||||
|
||||
if reqKey != bootactionKey {
|
||||
t.Logf("Request contained 'X-Bootaction-Key': %s\n", reqKey)
|
||||
w.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(r.URL.Path, bootactionID) {
|
||||
t.Logf("Requested URL path '%s' missing bootactionID\n", r.URL.Path)
|
||||
w.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
|
||||
reqBody, err := ioutil.ReadAll(r.Body)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Error reading test request: %s\n", err)
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
|
||||
var message BootactionMessage
|
||||
|
||||
err = json.Unmarshal(reqBody, &message)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Error parsing test request: %s\n", err)
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
|
||||
if message.Status != "" && message.Status != expectedResult {
|
||||
t.Logf("Did not receive expected result, instead received '%s'\n", message.Status)
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("Handled request: %s - %s\n", r.Method, r.URL.Path)
|
||||
t.Logf("Key: %s\n", reqKey)
|
||||
t.Logf("Body:\n %s\n", reqBody)
|
||||
w.WriteHeader(201)
|
||||
return
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(hf))
|
||||
|
||||
return ts
|
||||
}
|
121
go/src/baclient/config.go
Normal file
121
go/src/baclient/config.go
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseConfig() *ClientConfig {
|
||||
var clientConfig ClientConfig
|
||||
|
||||
parseFlagConfig(&clientConfig)
|
||||
|
||||
if clientConfig.showHelp {
|
||||
flag.PrintDefaults()
|
||||
return nil
|
||||
}
|
||||
|
||||
parseEnvConfig(&clientConfig)
|
||||
|
||||
if clientConfig.bootactionKey == "" {
|
||||
if clientConfig.bootactionKeyPath != "" {
|
||||
clientConfig.bootactionKey, _ = readKeyFile(clientConfig.bootactionKeyPath)
|
||||
}
|
||||
}
|
||||
|
||||
return &clientConfig
|
||||
}
|
||||
|
||||
func readKeyFile(keyPath string) (string, error) {
|
||||
keyFile, err := os.Open(keyPath)
|
||||
defer keyFile.Close()
|
||||
|
||||
if err == nil {
|
||||
var keyString string
|
||||
|
||||
bufReader := bufio.NewReader(keyFile)
|
||||
|
||||
keyString, err = bufReader.ReadString('\n')
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error reading key file: %s", err)
|
||||
} else {
|
||||
keyString = strings.Trim(keyString, "\n")
|
||||
return keyString, nil
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("Error opening key file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseFlagConfig(clientConfig *ClientConfig) {
|
||||
// If neither 's' or 'f' are specified, the API call will omit the 'status' field
|
||||
success := flag.Bool("s", false, "Does this message indicate bootaction success.")
|
||||
failure := flag.Bool("f", false, "Does this message indicate bootaction failure.")
|
||||
|
||||
if *failure {
|
||||
clientConfig.status = FAILURE
|
||||
} else if *success {
|
||||
clientConfig.status = SUCCESS
|
||||
}
|
||||
|
||||
flag.BoolVar(&clientConfig.showHelp, "h", false, "Show help and exit")
|
||||
flag.BoolVar(&clientConfig.isError, "e", false, "Does this message indicate error")
|
||||
flag.BoolVar(&clientConfig.proxyEnvironment, "np", false, "When wrapping an executable, should proxying the environment be disabled.")
|
||||
|
||||
flag.StringVar(&clientConfig.apiURL, "url", "", "Drydock API URL")
|
||||
flag.StringVar(&clientConfig.bootactionID, "id", "", "Bootaction ID")
|
||||
flag.StringVar(&clientConfig.bootactionKey, "key", "", "Bootaction ID")
|
||||
flag.StringVar(&clientConfig.bootactionKeyPath, "keyfile", "", "Absolute path to a file containing the API key")
|
||||
flag.StringVar(&clientConfig.message, "msg", "", "The detail message to record for the bootaction")
|
||||
flag.StringVar(&clientConfig.wrapExecutable, "exec", "", "The absolute path to an executable to run and report result.")
|
||||
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func parseEnvConfig(clientConfig *ClientConfig) {
|
||||
// for security, support reading the bootaction key from the environment
|
||||
baKey := os.Getenv("BOOTACTION_KEY")
|
||||
|
||||
if baKey != "" {
|
||||
clientConfig.bootactionKey = baKey
|
||||
}
|
||||
}
|
||||
|
||||
func (clientConfig *ClientConfig) validate() bool {
|
||||
valid := true
|
||||
|
||||
if clientConfig.bootactionID == "" {
|
||||
valid = false
|
||||
fmt.Printf("No Bootaction ID specified.\n")
|
||||
}
|
||||
|
||||
if clientConfig.bootactionKey == "" && clientConfig.bootactionKeyPath == "" {
|
||||
valid = false
|
||||
fmt.Printf("No Bootaction Key is specified.\n")
|
||||
}
|
||||
|
||||
if clientConfig.message == "" {
|
||||
valid = false
|
||||
fmt.Printf("Status message required.\n")
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
43
go/src/baclient/exec.go
Normal file
43
go/src/baclient/exec.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func executeAction(commandLine string, proxyEnv bool) bool {
|
||||
cmdParts := strings.Split(commandLine, " ")
|
||||
var cmdArgs []string
|
||||
|
||||
if len(cmdParts) > 1 {
|
||||
cmdArgs = cmdParts[1:]
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdParts[0], cmdArgs...)
|
||||
|
||||
if !proxyEnv {
|
||||
cmd.Env = []string{}
|
||||
}
|
||||
|
||||
err := cmd.Run()
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
46
go/src/baclient/types.go
Normal file
46
go/src/baclient/types.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a CLI for interacting with the Airship-Drydock Bootaction Signal
|
||||
// API
|
||||
|
||||
package main
|
||||
|
||||
const (
|
||||
SUCCESS = "success"
|
||||
FAILURE = "failure"
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
apiURL string
|
||||
bootactionID string
|
||||
bootactionKey string
|
||||
bootactionKeyPath string
|
||||
message string
|
||||
isError bool
|
||||
status string
|
||||
wrapExecutable string
|
||||
proxyEnvironment bool
|
||||
showHelp bool
|
||||
}
|
||||
|
||||
type BootactionMessage struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Details []BootactionDetail `json:"details"`
|
||||
}
|
||||
|
||||
type BootactionDetail struct {
|
||||
Message string `json:"message"`
|
||||
IsError bool `json:"error"`
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user