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

import (
4
	"bytes"
Lukas Burgey's avatar
Lukas Burgey committed
5
	"encoding/json"
6
	"fmt"
Lukas Burgey's avatar
Lukas Burgey committed
7
8
9
	"io/ioutil"
	"log"
	"net/http"
Lukas Burgey's avatar
Lukas Burgey committed
10
	"os"
11
	"time"
Lukas Burgey's avatar
Lukas Burgey committed
12

Lukas Burgey's avatar
Lukas Burgey committed
13
	"gopkg.in/alecthomas/kingpin.v2"
Lukas Burgey's avatar
Lukas Burgey committed
14
15
)

Lukas Burgey's avatar
Lukas Burgey committed
16
17
type (
	rabbitMQConfig struct {
Lukas Burgey's avatar
Lukas Burgey committed
18
19
		Exchanges []string `json:"exchanges"`
		Vhost     string   `json:"vhost"`
Lukas Burgey's avatar
Lukas Burgey committed
20
21
22
23
	}

	fetchedConfig struct {
		RabbitMQConfig rabbitMQConfig `json:"rabbitmq_config"`
Lukas Burgey's avatar
Lukas Burgey committed
24
		Site           string         `json:"site"`
Lukas Burgey's avatar
Lukas Burgey committed
25
26
27
	}

	config struct {
Lukas Burgey's avatar
Lukas Burgey committed
28
29
30
31
32
33
34
35
36
37
		Host     string `json:"host"`
		Username string `json:"username"`
		Password string `json:"password"`

		// Services provided at the site of this client
		// this is for  deployment per _service_
		Services []service `json:"services"`

		// GroupToServices maps a group name to services provided for this group
		// this is for  deployment per _group_
Lukas Burgey's avatar
Lukas Burgey committed
38
		GroupToServices map[string]([]service) `json:"group_to_services"`
Lukas Burgey's avatar
Lukas Burgey committed
39
40
41

		FetchIntervalString    string `json:"fetch_interval"`    // string parsed by time.ParseDuration
		ReconnectTimeoutString string `json:"reconnect_timeout"` // string parsed by time.ParseDuration
Lukas Burgey's avatar
Lukas Burgey committed
42
		NewTasks               chan task
43
		DoneTasks              chan taskExecution
Lukas Burgey's avatar
Lukas Burgey committed
44
45
		FetchInterval          time.Duration
		ReconnectTimeout       time.Duration
Lukas Burgey's avatar
Lukas Burgey committed
46
47
		RabbitMQConfig         rabbitMQConfig
		Site                   string
Lukas Burgey's avatar
Lukas Burgey committed
48
	}
49
50
51
52
53
54

	// strippedConfig is sent to the backend on startup
	strippedConfig struct {
		Services        []service              `json:"services"`
		GroupToServices map[string]([]service) `json:"group_to_services"`
	}
Lukas Burgey's avatar
Lukas Burgey committed
55
56
)

Lukas Burgey's avatar
Lukas Burgey committed
57
var (
Lukas Burgey's avatar
Lukas Burgey committed
58
	client = &http.Client{}
Lukas Burgey's avatar
Lukas Burgey committed
59
60
61
62
63
64
	app    = kingpin.New(
		"FEUDAL Client",
		"Client for the Federated User Credential Deployment Portal (FEUDAL)",
	).Author(
		"Lukas Burgey",
	).Version(
Lukas Burgey's avatar
Lukas Burgey committed
65
		"0.3.0",
Lukas Burgey's avatar
Lukas Burgey committed
66
	)
Lukas Burgey's avatar
Lukas Burgey committed
67
68
69
70
71
72
	configFile          = app.Arg("config", "Config file to file to use.").Required().String()
	scriptDebugging     = app.Flag("debug-scripts", "Display debugging info concerning executed scripts").Bool()
	backendDebugging    = app.Flag("debug-backend", "Display debugging info concerning the backend").Bool()
	brokerDebugging     = app.Flag("debug-broker", "Display debugging info concerning the message broker").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
73
74
)

75
func logError(err error, msg string) {
Lukas Burgey's avatar
Lukas Burgey committed
76
	if err != nil {
77
		log.Printf("[E] %s: %s", msg, err)
Lukas Burgey's avatar
Lukas Burgey committed
78
79
80
	}
}

81
func (c *config) syncConfig() (err error) {
Lukas Burgey's avatar
Lukas Burgey committed
82
83
84
85
86
87
88
89
90
91

	log.Printf("[Conf] Synchronising configuration with %v", c.Host)

	var (
		strippedConfigBytes []byte
		req                 *http.Request
		resp                *http.Response
		fetchedConfig       fetchedConfig
	)

92
	// we inform the backend which services we provide
Lukas Burgey's avatar
Lukas Burgey committed
93
	strippedConfigBytes, err = json.Marshal(strippedConfig{
94
95
96
97
98
99
100
101
		Services:        c.Services,
		GroupToServices: c.GroupToServices,
	})
	if err != nil {
		return
	}

	// update the services tracked by the backend
Lukas Burgey's avatar
Lukas Burgey committed
102
	req, err = http.NewRequest(
103
104
105
106
		"PUT",
		"https://"+c.Host+"/backend/clientapi/config",
		bytes.NewReader(strippedConfigBytes),
	)
Lukas Burgey's avatar
Lukas Burgey committed
107
108
109
110
	if err != nil {
		return
	}

111
	req.SetBasicAuth(c.Username, c.Password)
112
	req.Header.Set("Content-Type", "application/json")
Lukas Burgey's avatar
Lukas Burgey committed
113
	resp, err = client.Do(req)
Lukas Burgey's avatar
Lukas Burgey committed
114
115
116
117
118
119
120
121
122
	if err != nil {
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
123
	if resp.StatusCode != 200 {
Lukas Burgey's avatar
Lukas Burgey committed
124
		err = fmt.Errorf("Unable to sync configuration (response: %v)", resp.Status)
125
126
127
		return
	}

128
	err = json.Unmarshal(body, &fetchedConfig)
Lukas Burgey's avatar
Lukas Burgey committed
129
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
130
		err = fmt.Errorf("Unable to parse remote configuration: %s %s", err, body)
Lukas Burgey's avatar
Lukas Burgey committed
131
132
		return
	}
133

Lukas Burgey's avatar
Lukas Burgey committed
134
135
	c.RabbitMQConfig = fetchedConfig.RabbitMQConfig
	c.Site = fetchedConfig.Site
136

Lukas Burgey's avatar
Lukas Burgey committed
137
138
139
	return
}

140
func getConfig(configFile string) (c config, err error) {
141

Lukas Burgey's avatar
Lukas Burgey committed
142
	c.Log("Reading config file %s", configFile)
143

Lukas Burgey's avatar
Lukas Burgey committed
144
	bs, err := ioutil.ReadFile(configFile)
145
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
146
		c.Log("Error reading config file: %s", err)
147
148
149
		return
	}

Lukas Burgey's avatar
Lukas Burgey committed
150
151
	err = json.Unmarshal(bs, &c)
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
152
		c.Log("Error parsing config file: %s", err)
153
154
		return
	}
Lukas Burgey's avatar
Lukas Burgey committed
155

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	// check the config values
	if c.Host == "" {
		log.Fatalf("[Conf] No 'host' in config")
	}
	if c.Username == "" {
		log.Fatalf("[Conf] No 'user' in config")
	}
	if c.Password == "" {
		log.Fatalf("[Conf] No 'password' in config")
	}
	if c.FetchIntervalString == "" {
		log.Fatalf("[Conf] No 'fetch_interval' in config")
	}
	if c.ReconnectTimeoutString == "" {
		log.Fatalf("[Conf] No 'reconnect_timeout' in config")
Lukas Burgey's avatar
Lukas Burgey committed
171
	}
172
173

	// parse som of the config values
174
175
176
177
178
179
180
181
	if c.FetchInterval, err = time.ParseDuration(c.FetchIntervalString); err != nil {
		log.Printf("[Conf] Error parsing fetch interval: %s", err)
		return
	}
	if c.ReconnectTimeout, err = time.ParseDuration(c.ReconnectTimeoutString); err != nil {
		log.Printf("[Conf] Error parsing reconnect timeout: %s", err)
		return
	}
182
183

	// fetch the remote configuration
184
	err = c.syncConfig()
185
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
186
		log.Printf("[Conf] Error fetching remote config: %s", err)
187
188
		return
	}
Lukas Burgey's avatar
Lukas Burgey committed
189

Lukas Burgey's avatar
Lukas Burgey committed
190
191
	c.Log("Services: %s", c.Services)
	c.Log("Groups: %s", c.GroupToServices)
192

Lukas Burgey's avatar
Lukas Burgey committed
193
194
	// initialize the task queues
	c.NewTasks = make(chan task)
195
	c.DoneTasks = make(chan taskExecution)
Lukas Burgey's avatar
Lukas Burgey committed
196

Lukas Burgey's avatar
Lukas Burgey committed
197
198
	return
}
Lukas Burgey's avatar
Lukas Burgey committed
199
200

func main() {
Lukas Burgey's avatar
Lukas Burgey committed
201
	var err error
Lukas Burgey's avatar
Lukas Burgey committed
202

Lukas Burgey's avatar
Lukas Burgey committed
203
	// get arguments
Lukas Burgey's avatar
Lukas Burgey committed
204
205
	kingpin.MustParse(app.Parse(os.Args[1:]))

Lukas Burgey's avatar
Lukas Burgey committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
	if *debugAll {
		*brokerDebugging = true
		*scriptDebugging = true
		*backendDebugging = true
	}

	if *brokerDebugging {
		log.Printf("[Debug] broker debugging enabled")
	}
	if *scriptDebugging {
		log.Printf("[Debug] script debugging enabled")
	}
	if *backendDebugging {
		log.Printf("[Debug] backend debugging enabled")
	}
Lukas Burgey's avatar
Lukas Burgey committed
221
222
223
	if *sequentialExecution {
		log.Printf("[Debug] Executing tasks sequentially")
	}
Lukas Burgey's avatar
Lukas Burgey committed
224

Lukas Burgey's avatar
Lukas Burgey committed
225
	// read the config file
226
	c, err := getConfig(*configFile)
227
	if err != nil {
Lukas Burgey's avatar
Lukas Burgey committed
228
		log.Fatalf("[Exit] No valid config. Exiting")
229
230
	}

Lukas Burgey's avatar
Lukas Burgey committed
231
	if len(c.Services) == 0 && len(c.GroupToServices) == 0 {
Lukas Burgey's avatar
Lukas Burgey committed
232
233
		log.Printf("[P] Not starting pubsub because the are no services to subscribe to")
		return
Lukas Burgey's avatar
Lukas Burgey committed
234
235
	}

236
	// start task handler and responder
237
	go c.taskHandler()
238
	go c.taskResponder()
239

Lukas Burgey's avatar
Lukas Burgey committed
240
	consumer := c.consumer()
241
	defer consumer.close()
Lukas Burgey's avatar
Lukas Burgey committed
242

Lukas Burgey's avatar
Lukas Burgey committed
243
	consumer.startConsuming()
Lukas Burgey's avatar
Lukas Burgey committed
244

Lukas Burgey's avatar
Lukas Burgey committed
245
246
247
	// start the fetcher after the consuming starts
	// -> we miss nothing
	go c.taskFetcher()
Lukas Burgey's avatar
Lukas Burgey committed
248

Lukas Burgey's avatar
Lukas Burgey committed
249
250
251
	// run till killed
	forever := make(chan bool)
	<-forever
Lukas Burgey's avatar
Lukas Burgey committed
252
}