/
shell.go
124 lines (106 loc) · 2.81 KB
/
shell.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
package resource
import (
"os"
"os/exec"
"strings"
)
// Shell type is a resource which executes shell commands.
//
// The command that is to be executed should be idempotent.
// If the command that is to be executed is not idempotent on it's own,
// in order to achieve idempotency of the resource you should set the
// "creates" field to a filename that can be checked for existence.
//
// Example:
// sh = resource.shell.new("touch /tmp/foo")
// sh.creates = "/tmp/foo"
//
// Same example as the above one, but written in a different way.
//
// Example:
// sh = resource.shell.new("creates the /tmp/foo file")
// sh.command = "/usr/bin/touch /tmp/foo"
// sh.creates = "/tmp/foo"
type Shell struct {
Base
// Command to be executed. Defaults to the resource name.
Command string `luar:"command"`
// File to be checked for existence before executing the command.
Creates string `luar:"creates"`
// Mute flag indicates whether output from the command should be
// dislayed or suppressed
Mute bool `luar:"mute"`
}
// NewShell creates a new resource for executing shell commands
func NewShell(name string) (Resource, error) {
s := &Shell{
Base: Base{
Name: name,
Type: "shell",
State: "present",
Require: make([]string, 0),
PresentStatesList: []string{"present"},
AbsentStatesList: []string{"absent"},
Concurrent: true,
Subscribe: make(TriggerMap),
},
Command: name,
Creates: "",
Mute: false,
}
return s, nil
}
// Evaluate evaluates the state of the resource
func (s *Shell) Evaluate() (State, error) {
// Assumes that the command to be executed is idempotent
//
// Sets the current state to absent and wanted to be present,
// which will cause the command to be executed.
//
// If the command to be executed is not idempotent on it's own,
// in order to ensure idempotency we should specify a file,
// that can be checked for existence.
state := State{
Current: "absent",
Want: s.State,
Outdated: false,
}
if s.Creates != "" {
_, err := os.Stat(s.Creates)
if os.IsNotExist(err) {
state.Current = "absent"
} else {
state.Current = "present"
}
}
return state, nil
}
// Create executes the shell command
func (s *Shell) Create() error {
Logf("%s executing command\n", s.ID())
args := strings.Fields(s.Command)
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()
if !s.Mute {
for _, line := range strings.Split(string(out), "\n") {
Logf("%s %s\n", s.ID(), line)
}
}
return err
}
// Delete is a no-op
func (s *Shell) Delete() error {
return nil
}
// Update is a no-op
func (s *Shell) Update() error {
return nil
}
func init() {
item := ProviderItem{
Type: "shell",
Provider: NewShell,
Namespace: DefaultResourceNamespace,
}
RegisterProvider(item)
}