-
Notifications
You must be signed in to change notification settings - Fork 161
/
cpi_cmd_runner.go
129 lines (110 loc) · 3.32 KB
/
cpi_cmd_runner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package cloud
import (
"bytes"
"encoding/json"
"fmt"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
boshlog "github.com/cloudfoundry/bosh-utils/logger"
boshsys "github.com/cloudfoundry/bosh-utils/system"
)
type CmdInput struct {
Method string `json:"method"`
Arguments []interface{} `json:"arguments"`
Context CmdContext `json:"context"`
ApiVersion int `json:"api_version"`
}
type CmdContext struct {
DirectorID string `json:"director_uuid"`
Vm *VM `json:"vm,omitempty"`
}
type VM struct {
Stemcell *Stemcell `json:"stemcell,omitempty"`
}
type Stemcell struct {
ApiVersion int `json:"api_version,omitempty"`
}
func (c CmdContext) String() string {
bytes, err := json.Marshal(c)
if err != nil {
panic(fmt.Sprintf("Error stringifying CmdContext %#v: %s", c, err.Error()))
}
return fmt.Sprintf("CmdContext%s", string(bytes))
}
type CmdError struct {
Type string `json:"type"`
Message string `json:"message"`
OkToRetry bool `json:"ok_to_retry"`
}
func (e CmdError) String() string {
bytes, err := json.Marshal(e)
if err != nil {
panic(fmt.Sprintf("Error stringifying CmdError %#v: %s", e, err.Error()))
}
return fmt.Sprintf("CmdError%s", string(bytes))
}
type CmdOutput struct {
Result interface{} `json:"result"`
Error *CmdError `json:"error,omitempty"`
Log string `json:"log"`
}
//#go:generate counterfeiter -o fakes/fake_cpi_cmd_runner.go . CPICmdRunner
type CPICmdRunner interface {
Run(context CmdContext, method string, apiVersion int, args ...interface{}) (CmdOutput, error)
}
type cpiCmdRunner struct {
cmdRunner boshsys.CmdRunner
cpi CPI
logger boshlog.Logger
apiVersion int
logTag string
}
func NewCPICmdRunner(
cmdRunner boshsys.CmdRunner,
cpi CPI,
logger boshlog.Logger,
) CPICmdRunner {
return &cpiCmdRunner{
cmdRunner: cmdRunner,
cpi: cpi,
logger: logger,
logTag: "cpiCmdRunner",
}
}
func (r *cpiCmdRunner) Run(context CmdContext, method string, apiVersion int, args ...interface{}) (CmdOutput, error) {
if len(args) == 0 {
args = make([]interface{}, 0)
}
cmdInput := CmdInput{
Method: method,
Arguments: args,
Context: context,
ApiVersion: apiVersion,
}
inputBytes, err := json.Marshal(cmdInput)
if err != nil {
return CmdOutput{}, bosherr.WrapErrorf(err, "Marshalling external CPI command input %#v", cmdInput)
}
cmdPath := r.cpi.ExecutablePath()
cmd := boshsys.Command{
Name: cmdPath,
Env: map[string]string{
"BOSH_PACKAGES_DIR": r.cpi.PackagesDir,
"BOSH_JOBS_DIR": r.cpi.JobsDir,
"PATH": "/usr/local/bin:/usr/bin:/bin:/sbin",
},
UseIsolatedEnv: true,
Stdin: bytes.NewReader(inputBytes),
}
stdout, stderr, exitCode, err := r.cmdRunner.RunComplexCommand(cmd)
r.logger.Debug(r.logTag, "Exit Code %d when executing external CPI command '%s'\nSTDIN: '%s'\nSTDOUT: '%s'\nSTDERR: '%s'", exitCode, cmdPath, string(inputBytes), stdout, stderr)
if err != nil {
return CmdOutput{}, bosherr.WrapErrorf(err, "Executing external CPI command: '%s'", cmdPath)
}
cmdOutput := CmdOutput{}
err = json.Unmarshal([]byte(stdout), &cmdOutput)
if err != nil {
return CmdOutput{}, bosherr.WrapErrorf(err, "Unmarshalling external CPI command output: STDOUT: '%s', STDERR: '%s'", stdout, stderr)
}
r.logger.Debug(r.logTag, cmdOutput.Log)
return cmdOutput, err
}