diff --git a/go.mod b/go.mod index 1ec921391..6af660943 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( go.uber.org/atomic v1.2.0 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.7.1 // indirect + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect golang.org/x/text v0.3.2 // indirect diff --git a/test/_vagrant/data.json b/test/_vagrant/data.json new file mode 100644 index 000000000..7510ba2ca --- /dev/null +++ b/test/_vagrant/data.json @@ -0,0 +1,30 @@ +{ + "id": "ce2e62ed-826f-4485-a39f-a82bb74338e2", + "metadata": { + "facility": { + "facility_code": "onprem" + }, + "instance": {}, + "state": "" + }, + "network": { + "interfaces": [ + { + "dhcp": { + "arch": "x86_64", + "ip": { + "address": "192.168.1.5", + "gateway": "192.168.1.1", + "netmask": "255.255.255.248" + }, + "mac": "08:00:27:00:00:01", + "uefi": false + }, + "netboot": { + "allow_pxe": true, + "allow_workflow": true + } + } + ] + } +} diff --git a/test/_vagrant/failedWorkflow.tmpl b/test/_vagrant/failedWorkflow.tmpl new file mode 100644 index 000000000..25cbbec41 --- /dev/null +++ b/test/_vagrant/failedWorkflow.tmpl @@ -0,0 +1,18 @@ +version: '0.1' +name: packet_osie_provision +global_timeout: 600 +tasks: +- name: "timeout-task" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 10 + on-timeout: ["echo", "Timeout"] + on-failure: ["echo", "Failure"] + - name: "sleep-till-timeout" + image: bash + command: ["sleepys", "20"] + timeout: 6 + on-timeout: ["echo", "Timeout"] + on-failure: ["echo", "Failure"] diff --git a/test/_vagrant/hello-world.tmpl b/test/_vagrant/hello-world.tmpl new file mode 100644 index 000000000..fb5218830 --- /dev/null +++ b/test/_vagrant/hello-world.tmpl @@ -0,0 +1,10 @@ +version: "0.1" +name: hello_world_workflow +global_timeout: 600 +tasks: + - name: "hello world" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 60 diff --git a/test/_vagrant/timeout.tmpl b/test/_vagrant/timeout.tmpl new file mode 100644 index 000000000..c95aba8fd --- /dev/null +++ b/test/_vagrant/timeout.tmpl @@ -0,0 +1,18 @@ +version: '0.1' +name: packet_osie_provision +global_timeout: 600 +tasks: +- name: "timeout-task" + worker: "{{.device_1}}" + actions: + - name: "hello_world" + image: hello-world + timeout: 10 + on-timeout: ["echo", "Timeout"] + on-failure: ["echo", "Failure"] + - name: "sleep-till-timeout" + image: bash + command: ["sleep", "20"] + timeout: 6 + on-timeout: ["echo", "Timeout"] + on-failure: ["echo", "Failure"] diff --git a/test/_vagrant/vagrant_test.go b/test/_vagrant/vagrant_test.go index e1f80f9e0..5e316149c 100644 --- a/test/_vagrant/vagrant_test.go +++ b/test/_vagrant/vagrant_test.go @@ -3,8 +3,10 @@ package vagrant_test import ( "context" "encoding/json" + "io/ioutil" "net/http" "os" + "strings" "testing" "time" @@ -16,7 +18,7 @@ import ( "github.com/tinkerbell/tink/util" ) -func TestVagrantSetupGuide(t *testing.T) { +func TestSuccess(t *testing.T) { ctx := context.Background() machine, err := vagrant.Up(ctx, @@ -76,12 +78,14 @@ func TestVagrantSetupGuide(t *testing.T) { if err != nil { t.Fatal(err) } - err = registerHardware(ctx) + hwDataFile := "data.json" + + err = registerHardwares(ctx, hwDataFile) if err != nil { t.Fatal(err) } - templateID, err := registerTemplate(ctx) + templateID, err := registerTemplates(ctx, "hello-world.tmpl") if err != nil { t.Fatal(err) } @@ -130,6 +134,273 @@ func TestVagrantSetupGuide(t *testing.T) { } t.Fatal("Workflow never got to a complite state or it didn't make it on time (5m)") } +func TestTimeout(t *testing.T) { + ctx := context.Background() + + machine, err := vagrant.Up(ctx, + vagrant.WithLogger(t.Logf), + vagrant.WithMachineName("provisioner"), + vagrant.WithWorkdir("../../deploy/vagrant"), + ) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := machine.Destroy(ctx) + if err != nil { + t.Error(err) + } + }() + + _, err = machine.Exec(ctx, "cd /vagrant/deploy && source ../envrc && docker-compose up -d") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker pull hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker pull bash") + if err != nil { + t.Fatal(err) + } + _, err = machine.Exec(ctx, "docker tag hello-world 192.168.1.1/hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker tag bash 192.168.1.1/bash") + if err != nil { + t.Fatal(err) + } + _, err = machine.Exec(ctx, "docker push 192.168.1.1/hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker push 192.168.1.1/bash") + if err != nil { + t.Fatal(err) + } + + for ii := 0; ii < 5; ii++ { + resp, err := http.Get("http://localhost:42114/_packet/healthcheck") + if err != nil || resp.StatusCode != http.StatusOK { + if err != nil { + t.Logf("err tinkerbell healthcheck... retrying: %s", err) + } else { + t.Logf("err tinkerbell healthcheck... expected status code 200 got %d retrying", resp.StatusCode) + } + time.Sleep(10 * time.Second) + } + } + + t.Log("Tinkerbell is up and running") + + os.Setenv("TINKERBELL_CERT_URL", "http://127.0.0.1:42114/cert") + os.Setenv("TINKERBELL_GRPC_AUTHORITY", "127.0.0.1:42113") + client.Setup() + _, err = client.HardwareClient.All(ctx, &hardware.Empty{}) + if err != nil { + t.Fatal(err) + } + hwDataFile := "data.json" + + err = registerHardwares(ctx, hwDataFile) + if err != nil { + t.Fatal(err) + } + + templateID, err := registerTemplates(ctx, "timeout.tmpl") + if err != nil { + t.Fatal(err) + } + + t.Logf("templateID: %s", templateID) + + workflowID, err := createWorkflow(ctx, templateID) + if err != nil { + t.Fatal(err) + } + + t.Logf("WorkflowID: %s", workflowID) + + os.Setenv("VAGRANT_WORKER_GUI", "false") + worker, err := vagrant.Up(ctx, + vagrant.WithLogger(t.Logf), + vagrant.WithMachineName("worker"), + vagrant.WithWorkdir("../../deploy/vagrant"), + vagrant.RunAsync(), + ) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := worker.Destroy(ctx) + if err != nil { + t.Error(err) + } + }() + + for iii := 0; iii < 30; iii++ { + events, err := client.WorkflowClient.ShowWorkflowEvents(ctx, &workflow.GetRequest{ + Id: workflowID, + }) + if err != nil { + t.Fatal(err) + } + for event, err := events.Recv(); err == nil && event != nil; event, err = events.Recv() { + if event.ActionName == "hello_world" && event.ActionStatus == workflow.ActionState_ACTION_SUCCESS { + t.Logf("action %s SUCCESSFULL as expected", event.ActionName) + continue + } + if event.ActionName == "sleep-till-timeout" && event.ActionStatus == workflow.ActionState_ACTION_TIMEOUT { + t.Logf("action %s TIMEDOUT as expected", event.ActionName) + return + } + } + time.Sleep(5 * time.Second) + } + t.Fatal("Workflow never got to a complite state or it didn't make it on time (5m)") +} + +func TestFailed(t *testing.T) { + ctx := context.Background() + + machine, err := vagrant.Up(ctx, + vagrant.WithLogger(t.Logf), + vagrant.WithMachineName("provisioner"), + vagrant.WithWorkdir("../../deploy/vagrant"), + ) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := machine.Destroy(ctx) + if err != nil { + t.Error(err) + } + }() + + _, err = machine.Exec(ctx, "cd /vagrant/deploy && source ../envrc && docker-compose up -d") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker pull hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker pull bash") + if err != nil { + t.Fatal(err) + } + _, err = machine.Exec(ctx, "docker tag hello-world 192.168.1.1/hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker tag bash 192.168.1.1/bash") + if err != nil { + t.Fatal(err) + } + _, err = machine.Exec(ctx, "docker push 192.168.1.1/hello-world") + if err != nil { + t.Fatal(err) + } + + _, err = machine.Exec(ctx, "docker push 192.168.1.1/bash") + if err != nil { + t.Fatal(err) + } + + for ii := 0; ii < 5; ii++ { + resp, err := http.Get("http://localhost:42114/_packet/healthcheck") + if err != nil || resp.StatusCode != http.StatusOK { + if err != nil { + t.Logf("err tinkerbell healthcheck... retrying: %s", err) + } else { + t.Logf("err tinkerbell healthcheck... expected status code 200 got %d retrying", resp.StatusCode) + } + time.Sleep(10 * time.Second) + } + } + + t.Log("Tinkerbell is up and running") + + os.Setenv("TINKERBELL_CERT_URL", "http://127.0.0.1:42114/cert") + os.Setenv("TINKERBELL_GRPC_AUTHORITY", "127.0.0.1:42113") + client.Setup() + _, err = client.HardwareClient.All(ctx, &hardware.Empty{}) + if err != nil { + t.Fatal(err) + } + hwDataFile := "data.json" + + err = registerHardwares(ctx, hwDataFile) + if err != nil { + t.Fatal(err) + } + + templateID, err := registerTemplates(ctx, "failedWorkflow.tmpl") + if err != nil { + t.Fatal(err) + } + + t.Logf("templateID: %s", templateID) + + workflowID, err := createWorkflow(ctx, templateID) + if err != nil { + t.Fatal(err) + } + + t.Logf("WorkflowID: %s", workflowID) + + os.Setenv("VAGRANT_WORKER_GUI", "false") + worker, err := vagrant.Up(ctx, + vagrant.WithLogger(t.Logf), + vagrant.WithMachineName("worker"), + vagrant.WithWorkdir("../../deploy/vagrant"), + vagrant.RunAsync(), + ) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := worker.Destroy(ctx) + if err != nil { + t.Error(err) + } + }() + + for iii := 0; iii < 30; iii++ { + events, err := client.WorkflowClient.ShowWorkflowEvents(ctx, &workflow.GetRequest{ + Id: workflowID, + }) + if err != nil { + t.Fatal(err) + } + for event, err := events.Recv(); err == nil && event != nil; event, err = events.Recv() { + if event.ActionName == "hello_world" && event.ActionStatus == workflow.ActionState_ACTION_SUCCESS { + t.Logf("action %s SUCCESSFULL as expected", event.ActionName) + continue + } + if event.ActionName == "sleep-till-timeout" && event.ActionStatus == workflow.ActionState_ACTION_FAILED { + t.Logf("action %s FAILED as expected", event.ActionName) + return + } + } + time.Sleep(5 * time.Second) + } + t.Fatal("Workflow never got to a complite state or it didn't make it on time (5m)") +} func createWorkflow(ctx context.Context, templateID string) (string, error) { res, err := client.WorkflowClient.CreateWorkflow(ctx, &workflow.CreateRequest{ @@ -142,63 +413,49 @@ func createWorkflow(ctx context.Context, templateID string) (string, error) { return res.Id, nil } -func registerTemplate(ctx context.Context) (string, error) { - resp, err := client.TemplateClient.CreateTemplate(ctx, &template.WorkflowTemplate{ - Name: "hello-world", - Data: `version: "0.1" -name: hello_world_workflow -global_timeout: 600 -tasks: - - name: "hello world" - worker: "{{.device_1}}" - actions: - - name: "hello_world" - image: hello-world - timeout: 60`, - }) +func readData(file string) ([]byte, error) { + f, err := os.Open(file) if err != nil { - return "", err + return []byte(""), err } + defer f.Close() - return resp.Id, nil + data, err := ioutil.ReadAll(f) + if err != nil { + return []byte(""), err + } + return data, nil } -func registerHardware(ctx context.Context) error { - data := []byte(`{ - "id": "ce2e62ed-826f-4485-a39f-a82bb74338e2", - "metadata": { - "facility": { - "facility_code": "onprem" - }, - "instance": {}, - "state": "" - }, - "network": { - "interfaces": [ - { - "dhcp": { - "arch": "x86_64", - "ip": { - "address": "192.168.1.5", - "gateway": "192.168.1.1", - "netmask": "255.255.255.248" - }, - "mac": "08:00:27:00:00:01", - "uefi": false - }, - "netboot": { - "allow_pxe": true, - "allow_workflow": true - } - } - ] - } -}`) - hw := util.HardwareWrapper{Hardware: &hardware.Hardware{}} - err := json.Unmarshal(data, &hw) +// push hardware data through file +func registerHardwares(ctx context.Context, hwDatafile string) error { + //for _, hwFile := range hwDataFiles { + //filepath := "../data/hardware/" + hwFile + data, err := readData(hwDatafile) if err != nil { return err } + hw := util.HardwareWrapper{Hardware: &hardware.Hardware{}} + if err := json.Unmarshal(data, &hw); err != nil { + return err + } _, err = client.HardwareClient.Push(context.Background(), &hardware.PushRequest{Data: hw.Hardware}) - return err + if err != nil { + return err + } + //} + return nil +} + +func registerTemplates(ctx context.Context, templateFile string) (string, error) { + data, err := readData(templateFile) + if err != nil { + return "", err + } + name := strings.SplitN(templateFile, ".", -1)[0] + resp, err := client.TemplateClient.CreateTemplate(ctx, &template.WorkflowTemplate{Name: name, Data: string(data)}) + if err != nil { + return "", err + } + return resp.Id, nil }