main.go 6.8 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1
2
3
package main

import (
4
	"fmt"
Lukas Burgey's avatar
Lukas Burgey committed
5
6
7
	"io/ioutil"
	"log"
	"net/http"
Lukas Burgey's avatar
Lukas Burgey committed
8
	"os"
9
10
	"path"
	"path/filepath"
Lukas Burgey's avatar
Lukas Burgey committed
11

Lukas Burgey's avatar
Lukas Burgey committed
12
	"git.scc.kit.edu/feudal/feudalClient/config"
Lukas Burgey's avatar
Lukas Burgey committed
13
	deps "git.scc.kit.edu/feudal/feudalClient/deployments"
Lukas Burgey's avatar
Lukas Burgey committed
14
15
16
17
18
	"git.scc.kit.edu/feudal/feudalClient/sink"
	"git.scc.kit.edu/feudal/feudalClient/sink/script"
	"git.scc.kit.edu/feudal/feudalClient/source"
	"git.scc.kit.edu/feudal/feudalClient/source/amqp"
	"git.scc.kit.edu/feudal/feudalClient/source/rest"
Lukas Burgey's avatar
Lukas Burgey committed
19
	kingpin "gopkg.in/alecthomas/kingpin.v2"
Lukas Burgey's avatar
Lukas Burgey committed
20
21
22
)

var (
23
24
25
26
	// version and scriptsVersion are set via: go build -ldflags '-X main.version=foobar -X main.scriptsVersion=foobar
	version        = ""
	scriptsVersion = ""

Lukas Burgey's avatar
Lukas Burgey committed
27
	app = kingpin.New(
Lukas Burgey's avatar
Lukas Burgey committed
28
29
30
31
		"FEUDAL Client",
		"Client for the Federated User Credential Deployment Portal (FEUDAL)",
	).Author(
		"Lukas Burgey",
32
33
	).Version(fmt.Sprintf("feudalClient:  %s\nfeudalScripts: %s", version, scriptsVersion))

Lukas Burgey's avatar
Lukas Burgey committed
34
35
36
37
38
39
	cmdStart         = app.Command("start", "Starts the client in its normal operation mode.").Default()
	cmdDeregister    = app.Command("deregister", "Before disabling the client: Use deregister to inform the backend that the client ceases operation.")
	cmdSchema        = app.Command("schema", "Can be used to generate a JSON schema (Draft 4, see json-schema.org) for the validation of script inputs, outputs and the config of this client.")
	printSelector    = cmdSchema.Arg("selector", "Select the schema to generate. Options: input, output, config").Required().Enum("input", "output", "config")
	cmdValidate      = app.Command("validate", "Validate a input/output/config json. The json needs to be passed via stdin.")
	validateSelector = cmdValidate.Arg("selector", "Select the schema to validate against. Options: input, output, config").Required().Enum("input", "output", "config")
Lukas Burgey's avatar
Lukas Burgey committed
40

Lukas Burgey's avatar
Lukas Burgey committed
41
	configFile          = app.Flag("config", "Config file to use.").Short('c').String()
Lukas Burgey's avatar
Lukas Burgey committed
42
43
44
45
	scriptDebugging     = app.Flag("debug-scripts", "Display debugging info concerning executed scripts").Bool()
	backendDebugging    = app.Flag("debug-backend", "Display debugging info concerning the backend").Bool()
	debugAll            = app.Flag("debug", "Display all debugging info").Bool()
	sequentialExecution = app.Flag("seq", "Execute tasks sequentially").Bool()
Lukas Burgey's avatar
Lukas Burgey committed
46
47
)

Lukas Burgey's avatar
Lukas Burgey committed
48
func deregister(conf *config.Config) {
Lukas Burgey's avatar
Lukas Burgey committed
49
50
51
52
53
54
	var (
		err  error
		req  *http.Request
		resp *http.Response
	)
	log.Printf("Deregistering this client at the backend")
Lukas Burgey's avatar
Lukas Burgey committed
55

Lukas Burgey's avatar
Lukas Burgey committed
56
57
	req, err = http.NewRequest(
		"PUT",
58
		"https://"+conf.Hostname+"/client/deregister",
Lukas Burgey's avatar
Lukas Burgey committed
59
60
61
62
		nil,
	)
	if err != nil {
		log.Fatalf("Unable to deregister: %s", err)
Lukas Burgey's avatar
Lukas Burgey committed
63
	}
Lukas Burgey's avatar
Lukas Burgey committed
64
	req.SetBasicAuth(conf.Username, conf.Password)
Lukas Burgey's avatar
Lukas Burgey committed
65

Lukas Burgey's avatar
Lukas Burgey committed
66
	resp, err = (&http.Client{}).Do(req)
67
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
68
		log.Fatalf("Unable to deregister: %s", err)
69
	}
Lukas Burgey's avatar
Lukas Burgey committed
70
71
72
73
74
75
76
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Cannot read response body")
	}
	log.Printf("Response: %s", body)
}
77

Lukas Burgey's avatar
Lukas Burgey committed
78
79
func start(conf *config.Config) {
	if len(conf.EntitlementToServiceIDs) == 0 && len(conf.GroupToServiceIDs) == 0 {
Lukas Burgey's avatar
Lukas Burgey committed
80
81
		log.Printf("[P] Not starting pubsub because the are no services to subscribe to")
		return
Lukas Burgey's avatar
Lukas Burgey committed
82
83
	}

Lukas Burgey's avatar
Lukas Burgey committed
84
85
86
	// wire the pipes from sources to sink(s)
	sources := []source.Source{new(rest.Source), new(amqp.Source)}
	srcPipes := make([]<-chan deps.Dep, len(sources))
87

Lukas Burgey's avatar
Lukas Burgey committed
88
89
	sinks := []sink.Sink{new(script.Sink)}
	sinkPipe := make(chan deps.Dep)
Lukas Burgey's avatar
Lukas Burgey committed
90

Lukas Burgey's avatar
Lukas Burgey committed
91
	var err error
Lukas Burgey's avatar
Lukas Burgey committed
92
93
94
95
96
97
98
99
100
101
102
103
104
	for _, sink := range sinks {
		sink.Init(conf)
		err = sink.Connect((<-chan deps.Dep)(sinkPipe))
		if err != nil {
			log.Printf("Error connecting sink: %s", err)
		}
	}
	for i, src := range sources {
		src.Init(conf)
		srcPipes[i], err = src.Connect()
		if err != nil {
			log.Printf("Error connecting source: %s", err)
		}
Lukas Burgey's avatar
Lukas Burgey committed
105
	}
Lukas Burgey's avatar
Lukas Burgey committed
106

Lukas Burgey's avatar
Lukas Burgey committed
107
108
109
110
111
112
113
114
115
116
	// TODO rework
	var received deps.Dep
	for {
		select {
		case received = <-srcPipes[0]:
			sinkPipe <- received
		case received = <-srcPipes[1]:
			sinkPipe <- received
		}
	}
Lukas Burgey's avatar
Lukas Burgey committed
117
}
Lukas Burgey's avatar
Lukas Burgey committed
118

Lukas Burgey's avatar
Lukas Burgey committed
119
func prepareDebugging(conf *config.Config) {
Lukas Burgey's avatar
Lukas Burgey committed
120
121

	// Override the debug configuration with parameters
Lukas Burgey's avatar
Lukas Burgey committed
122
	if *debugAll {
123
124
		conf.Debug.Backend = true
		conf.Debug.Scripts = true
Lukas Burgey's avatar
Lukas Burgey committed
125
126
	}
	if *scriptDebugging {
Lukas Burgey's avatar
Lukas Burgey committed
127
		conf.Debug.Scripts = true
Lukas Burgey's avatar
Lukas Burgey committed
128
129
	}
	if *backendDebugging {
Lukas Burgey's avatar
Lukas Burgey committed
130
		conf.Debug.Backend = true
Lukas Burgey's avatar
Lukas Burgey committed
131
132
	}
	if *sequentialExecution {
Lukas Burgey's avatar
Lukas Burgey committed
133
		conf.Debug.Sequential = true
Lukas Burgey's avatar
Lukas Burgey committed
134
135
	}

Lukas Burgey's avatar
Lukas Burgey committed
136
137
138
139
140
141
142
143
	if conf.Debug.Scripts {
		log.Printf("[Debug] script debugging enabled")
	}
	if conf.Debug.Backend {
		log.Printf("[Debug] backend debugging enabled")
	}
	if conf.Debug.Sequential {
		log.Printf("[Debug] Executing tasks sequentially")
Lukas Burgey's avatar
Lukas Burgey committed
144
	}
Lukas Burgey's avatar
Lukas Burgey committed
145
146
}

147
148
149
150
151
152
153
154
155
156
157
158
159
// search for a config file
// dir pattern: $HOME/.config -> /etc/feudal -> $CWD
// file pattern: *.yaml -> *.json
func findConfigFile(arg string) (handle *os.File, err error) {
	globString := func(gs string) string {
		var files []string
		files, err = filepath.Glob(gs)
		if err == nil && len(files) > 0 {
			log.Printf("Globbed: %v", files)
			_, err = os.Stat(files[0])
			if err == nil {
				return files[0]
			}
Lukas Burgey's avatar
Lukas Burgey committed
160
			log.Printf("Error opening: %s", err)
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
		}
		return ""
	}
	globDir := func(ds string) (name string) {
		name = globString(path.Join(ds, "*.yaml"))
		if name != "" {
			return
		}
		name = globString(path.Join(ds, "*.json"))
		if name != "" {
			return
		}
		return
	}
	findName := func() string {
		name := ""
		if *configFile != "" {
			// check if file exists
			_, err = os.Stat(*configFile)
			if err == nil {
				return *configFile
			}
Lukas Burgey's avatar
Lukas Burgey committed
183
			log.Printf("Error opening: %s", err)
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
		}

		var home, cwd string

		// search $HOME/.config
		if home, err = os.UserHomeDir(); err == nil {
			name = globDir(path.Join(home, ".config/feudal"))
			if name != "" {
				return name
			}
		}
		name = globDir("/etc/feudal")
		if name != "" {
			return name
		}

		cwd, err = os.Getwd()
		if err == nil {
			name = globDir(cwd)
			if name != "" {
				return name
			}
		}
		return ""
	}

	firstName := findName()

	if firstName == "" {
		err = fmt.Errorf(`No config file!
Provide -c/--config or place a config json/yaml in $HOME/.config/feudal or /etc/feudal or the current working directory`)
		return
	}

	return os.Open(firstName)
}

Lukas Burgey's avatar
Lukas Burgey committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
func main() {
	var err error

	// get arguments
	cmd := kingpin.MustParse(app.Parse(os.Args[1:]))

	// Execute commands that don't need the config
	switch cmd {
	case "schema":
		printSchema(*printSelector)
		return
	case "validate":
		result, err := validateJSON(*validateSelector, os.Stdin)
		if err != nil {
			log.Fatal(err)
		}
		printValidationResult(result)
		return
	}

241
242
	// search appropriate config
	handle, err := findConfigFile(*configFile)
243
244
245
	if err != nil {
		log.Fatalf("[Conf] Error opening config file: %s", err)
	}
246
	log.Printf("[Conf] Opened config file %s", handle.Name())
247

248
249
	// read the config file
	conf, err := config.ReadConfig(handle, handle.Name())
Lukas Burgey's avatar
Lukas Burgey committed
250
251
252
253
254
	if err != nil {
		log.Fatalf("[Conf] %s", err)
	}

	prepareDebugging(conf)
Lukas Burgey's avatar
Lukas Burgey committed
255
256
257

	switch cmd {
	case "start":
Lukas Burgey's avatar
Lukas Burgey committed
258
		// sync the config with the backend
Lukas Burgey's avatar
Lukas Burgey committed
259
		if err := conf.Sync(); err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
260
			log.Printf("[Conf] Error synchronizing configuration: %s", err)
Lukas Burgey's avatar
Lukas Burgey committed
261
262
			return
		}
Lukas Burgey's avatar
Lukas Burgey committed
263
		start(conf)
Lukas Burgey's avatar
Lukas Burgey committed
264
265

	case "deregister":
Lukas Burgey's avatar
Lukas Burgey committed
266
		deregister(conf)
Lukas Burgey's avatar
Lukas Burgey committed
267
268
	}
}