This repository has been archived by the owner on Jun 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
marathon.go
128 lines (109 loc) · 3.76 KB
/
marathon.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
package main
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"strings"
)
// MarathonTaskResponse is the /v2/apps/{app_id}/tasks response struct
type MarathonTaskResponse struct {
ID string
Version string
}
// MarathonAppResponse is the /v2/apps/{app_id} response struct
type MarathonAppResponse struct {
ID, Version string
Tasks []MarathonTaskResponse
}
// MarathonAppsResponse is wrapping MarathonAppResponse
type MarathonAppsResponse struct {
App MarathonAppResponse
}
// MarathonVersionResponse is the /v2/apps/{app_id}/versions/{version}
// response struct
type MarathonVersionResponse struct {
ID, Version string
Env map[string]string
}
// MarathonApp is the result struct from getMarathonApp
type MarathonApp struct {
ID, Version, TaskID string
DeployKey *[32]byte
ServiceKey *[32]byte
Env map[string]string
}
func verifyRunningTask(appID string, appVersion string, taskID string, body []byte) (bool, error) {
// Parse the JSON response
var app MarathonAppsResponse
err := json.Unmarshal(body, &app)
if err != nil || app.App.ID != appID {
return false, fmt.Errorf("Failed to parse Marathon JSON response (%s): %s", err, string(body))
}
// Check running tasks for this app and verify that the given taskId is present
isActiveTaskid := false
for _, task := range app.App.Tasks {
if task.ID == taskID && task.Version == appVersion {
isActiveTaskid = true
break
}
}
if !isActiveTaskid {
return false, errors.New("Given taskId is not running (bug or hacking attempt?)")
}
return true, nil
}
func parseApplicationVersion(appID string, appVersion string, taskID string, body []byte) (*MarathonApp, error) {
// Parse the JSON response
var taskVersion MarathonVersionResponse
err := json.Unmarshal(body, &taskVersion)
if err != nil || taskVersion.ID != appID || taskVersion.Version != appVersion {
return nil, fmt.Errorf("Failed to parse Marathon JSON response (%s): %s", err, string(body))
}
// Extract the deploy public key
encodedDeployKey, ok := taskVersion.Env["DEPLOY_PUBLIC_KEY"]
if !ok {
return nil, errors.New("App is missing $DEPLOY_PUBLIC_KEY in the Marathon config \"env\" section")
}
deployKey, err := pemDecode(encodedDeployKey)
if err != nil {
return nil, fmt.Errorf("Failed to decode $DEPLOY_PUBLIC_KEY (%s)", err)
}
// Extract the optional service public key
encodedServiceKey, ok := taskVersion.Env["SERVICE_PUBLIC_KEY"]
var serviceKey *[32]byte
if ok {
serviceKey, err = pemDecode(encodedServiceKey)
if err != nil {
return nil, fmt.Errorf("Failed to decode $SERVICE_PUBLIC_KEY (%s)", err)
}
}
return &MarathonApp{ID: taskVersion.ID, Version: taskVersion.Version,
TaskID: taskID, DeployKey: deployKey, ServiceKey: serviceKey,
Env: taskVersion.Env}, nil
}
func getMarathonApp(marathonURL string, appID string, appVersion string, taskID string) (*MarathonApp, error) {
// Validate that given taskId is actually still running (old deploy keys shouldn't be allows to access any secrets)
{
// Fetch the list of running tasks for this app
url := fmt.Sprintf("%s/v2/apps/%s?embed=apps.tasks", marathonURL,
strings.Replace(strings.Replace(url.QueryEscape(strings.TrimLeft(appID, "/")), "..", "", -1), "%2F", "/", -1))
body, err := httpGet(url)
if err != nil {
return nil, err
}
ok, err := verifyRunningTask(appID, appVersion, taskID, body)
if !ok {
return nil, err
}
}
// Fetch the exact app config version for this task
url := fmt.Sprintf("%s/v2/apps/%s/versions/%s", marathonURL,
strings.Replace(strings.Replace(url.QueryEscape(strings.TrimLeft(appID, "/")), "..", "", -1), "%2F", "/", -1),
url.QueryEscape(appVersion))
body, err := httpGet(url)
if err != nil {
return nil, err
}
return parseApplicationVersion(appID, appVersion, taskID, body)
}