Skip to content

Commit 09102b2

Browse files
authored
Merge pull request #18 from getnelson/blueprints/create
Add support for creating and inspecting blueprints
2 parents 1554f90 + 84cb281 commit 09102b2

File tree

3 files changed

+189
-1
lines changed

3 files changed

+189
-1
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ $ nelson namespace create --datacenter dc123 --namespace foobar
9494
$ nelson ns create --dc dc123 --ns foobar
9595
```
9696

97+
### Blueprint Operations
98+
99+
```
100+
# proof a template before commiting it to nelson
101+
$ nelson blueprint proof -f /path/to/gpu-kubernetes.mustache
102+
103+
# create a blueprint from a template on your client host
104+
$ nelson blueprint create -n somename -f /path/to/gpu-kubernetes.mustache
105+
106+
# inspect a template
107+
nelson blueprint inspect -n cpu-cron-job
108+
nelson blueprint inspect -n cpu-cron-job -r HEAD
109+
nelson blueprint inspect cpu-cron-job@HEAD
110+
nelson blueprint inspect cpu-cron-job@6
111+
```
112+
97113
### Unit Operations
98114

99115
```

src/github.com/getnelson/nelson/blueprint.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"strconv"
2525
)
2626

27+
/////////////////// PROOFING BLUEPRINTS ///////////////////
28+
2729
/*
2830
* {
2931
* "content": "CAgICAgIHBsYW5zOg0KICAgICAgICAgIC0gZGVmYXVsdA=="
@@ -59,3 +61,79 @@ func ProofBlueprint(req ProofBlueprintWire, http *gorequest.SuperAgent, cfg *Con
5961
return "", errs
6062
}
6163
}
64+
65+
/////////////////// CREATING BLUEPRINTS ///////////////////
66+
67+
/*
68+
* {
69+
* "name": "use-nvidia-1080ti",
70+
* "description": "only scheudle on nodes with nvida 1080ti hardware"
71+
* "sha256": "1e34a423ebe1fafeda8277386ede3263b01357e490b124b69bc0bfb493e64140"
72+
* "template": "<base64 encoded template>"
73+
* }
74+
*/
75+
type CreateBlueprintResponse struct {
76+
Name string `json:"name"`
77+
Description string `json:"description"`
78+
Revision string `json:"revision"`
79+
Sha256 string `json:"sha256"`
80+
Template string `json:"template"`
81+
CreatedAt int64 `json:"created_at"`
82+
}
83+
84+
type CreateBlueprintRequest struct {
85+
Name string `json:"name"`
86+
Description string `json:"description"`
87+
Sha256 string `json:"sha256"`
88+
Template string `json:"template"`
89+
}
90+
91+
func CreateBlueprint(req CreateBlueprintRequest, http *gorequest.SuperAgent, cfg *Config) (out CreateBlueprintResponse, err []error) {
92+
93+
r, body, errs := AugmentRequest(
94+
http.Post(cfg.Endpoint+"/v1/blueprints"), cfg).Send(req).EndBytes()
95+
96+
var result CreateBlueprintResponse
97+
98+
if errs != nil {
99+
return result, errs
100+
}
101+
102+
if r.StatusCode/100 != 2 {
103+
errs = append(errs, errors.New("Unexpectedly recieved a "+strconv.Itoa(r.StatusCode)+" reponse from the server."))
104+
return result, errs
105+
} else {
106+
if err := json.Unmarshal(body, &result); err != nil {
107+
errs = append(errs, errors.New("Unexpected response from Nelson server"))
108+
}
109+
return result, errs
110+
}
111+
}
112+
113+
/////////////////// LISTING BLUEPRINTS ///////////////////
114+
115+
// func ListBlueprints(http *gorequest.SuperAgent, cfg *Config) (out CreateBlueprintResponse, err []error) {
116+
// }
117+
118+
/////////////////// INSPECTING BLUEPRINTS ///////////////////
119+
120+
func InspectBlueprint(namedRevision string, http *gorequest.SuperAgent, cfg *Config) (out CreateBlueprintResponse, err []error) {
121+
r, body, errs := AugmentRequest(
122+
http.Get(cfg.Endpoint+"/v1/blueprints/"+namedRevision), cfg).EndBytes()
123+
124+
var result CreateBlueprintResponse
125+
126+
if errs != nil {
127+
return result, errs
128+
}
129+
130+
if r.StatusCode/100 != 2 {
131+
errs = append(errs, errors.New("Unexpectedly recieved a "+strconv.Itoa(r.StatusCode)+" reponse from the server."))
132+
return result, errs
133+
} else {
134+
if err := json.Unmarshal(body, &result); err != nil {
135+
errs = append(errs, errors.New("Unexpected response from Nelson server"))
136+
}
137+
return result, errs
138+
}
139+
}

src/github.com/getnelson/nelson/main.go

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package main
1818

1919
import (
20+
"crypto/sha256"
2021
"encoding/base64"
2122
"fmt"
2223
"github.com/parnurzeal/gorequest"
@@ -66,6 +67,7 @@ func main() {
6667
var selectedNoGrace bool
6768
var repository string
6869
var owner string
70+
var selectedName string
6971

7072
app.Flags = []cli.Flag{
7173
cli.IntFlag{
@@ -138,7 +140,7 @@ func main() {
138140
cli.StringFlag{
139141
Name: "source, file, f",
140142
Value: "",
141-
Usage: "The blueprint template file to proof",
143+
Usage: "Path to the blueprint template file to proof",
142144
Destination: &selectedManifest,
143145
},
144146
},
@@ -165,6 +167,98 @@ func main() {
165167
return nil
166168
},
167169
},
170+
{
171+
Name: "create",
172+
Usage: "Commit the template to Nelson",
173+
Flags: []cli.Flag{
174+
cli.StringFlag{
175+
Name: "name, n",
176+
Value: "",
177+
Usage: "Name of Blueprint",
178+
Destination: &selectedName,
179+
},
180+
cli.StringFlag{
181+
Name: "description, d",
182+
Value: "",
183+
Usage: "Description of Blueprint templte",
184+
Destination: &description,
185+
},
186+
cli.StringFlag{
187+
Name: "source, file, f",
188+
Value: "",
189+
Usage: "Path to the Blueprint template file",
190+
Destination: &selectedManifest,
191+
},
192+
},
193+
Action: func(c *cli.Context) error {
194+
if len(selectedManifest) <= 0 {
195+
return cli.NewExitError("No blueprint template file specified.", 1)
196+
}
197+
manifest, err := ioutil.ReadFile(selectedManifest)
198+
if err != nil {
199+
return cli.NewExitError("Could not read "+selectedManifest, 1)
200+
}
201+
sum := sha256.Sum256(manifest)
202+
sha := fmt.Sprintf("%x", sum)
203+
manifestBase64 := base64.StdEncoding.EncodeToString(manifest)
204+
wire := CreateBlueprintRequest{
205+
Name: selectedName,
206+
Description: description,
207+
Sha256: sha,
208+
Template: manifestBase64,
209+
}
210+
211+
pi.Start()
212+
cfg := LoadDefaultConfigOrExit(http)
213+
r, e := CreateBlueprint(wire, http, cfg)
214+
pi.Stop()
215+
if e != nil {
216+
return cli.NewExitError("Unable to create blueprint.", 1)
217+
} else {
218+
fmt.Println("Successfully created blueprint " + r.Name + "@" + r.Revision + ".")
219+
fmt.Println("@HEAD will point to revision " + r.Revision + " until future revisions are committed.")
220+
}
221+
return nil
222+
},
223+
},
224+
{
225+
Name: "inspect",
226+
Usage: "Yield the template for a given revision",
227+
Flags: []cli.Flag{
228+
cli.StringFlag{
229+
Name: "name, n",
230+
Value: "",
231+
Usage: "Name of Blueprint",
232+
Destination: &selectedName,
233+
},
234+
cli.StringFlag{
235+
Name: "revision, r",
236+
Value: "",
237+
Usage: "Specific revision of Blueprint template; e.g HEAD, 3, 5",
238+
Destination: &selectedVersion,
239+
},
240+
},
241+
Action: func(c *cli.Context) error {
242+
var bpName string
243+
namedRevision := c.Args().First()
244+
if len(namedRevision) > 0 && len(selectedName) < 1 {
245+
bpName = namedRevision
246+
} else {
247+
bpName = selectedName + "@" + selectedVersion
248+
}
249+
250+
pi.Start()
251+
cfg := LoadDefaultConfigOrExit(http)
252+
r, e := InspectBlueprint(bpName, http, cfg)
253+
pi.Stop()
254+
if e != nil {
255+
return cli.NewExitError("Unable to create blueprint.", 1)
256+
} else {
257+
fmt.Println(r.Template)
258+
}
259+
return nil
260+
},
261+
},
168262
},
169263
},
170264
////////////////////////////// DATACENTER //////////////////////////////////

0 commit comments

Comments
 (0)