func TestCloudHandler(t *testing.T) { content := `#cloud-config coreos: etcd2: name: {{.uuid}} units: - name: {{.service_name}} ` expected := `#cloud-config coreos: etcd2: name: a1b2c3d4 units: - name: etcd2 ` store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: fake.Profile}, CloudConfigs: map[string]string{fake.Profile.CloudId: content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.cloudHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Cloud config is rendered with Group metadata and selectors assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, expected, w.Body.String()) }
func TestIgnitionHandler_MissingTemplateMetadata(t *testing.T) { content := ` ignition_version: 1 systemd: units: - name: {{.missing_key}} enable: true ` store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: fake.Profile}, IgnitionConfigs: map[string]string{fake.Profile.IgnitionId: content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.ignitionHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Ignition template rendering errors because "missing_key" is not // present in the Group metadata assert.Equal(t, http.StatusNotFound, w.Code) }
func TestGenericHandler(t *testing.T) { content := `#foo-bar-baz template UUID={{.uuid}} SERVICE={{.service_name}} ` expected := `#foo-bar-baz template UUID=a1b2c3d4 SERVICE=etcd2 ` store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: fake.Profile}, GenericConfigs: map[string]string{fake.Profile.GenericId: content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.genericHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Generic config is rendered with Group metadata and selectors assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, expected, w.Body.String()) }
func TestIgnitionHandler_V2JSON(t *testing.T) { content := `{"ignition":{"version":"2.0.0","config":{}},"storage":{},"systemd":{"units":[{"name":"etcd2.service","enable":true},{"name":"a1b2c3d4.service","enable":true}]},"networkd":{},"passwd":{}}` profile := &storagepb.Profile{ Id: fake.Group.Profile, IgnitionId: "file.ign", } store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: profile}, IgnitionConfigs: map[string]string{"file.ign": content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.ignitionHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Ignition template is rendered with Group metadata and selectors // - Rendered Ignition template is parsed as JSON // - Ignition Config served as JSON assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType)) assert.Equal(t, expectedIgnitionV2, w.Body.String()) }
func TestIgnitionHandler_V2YAML(t *testing.T) { content := ` systemd: units: - name: {{.service_name}}.service enable: true - name: {{.uuid}}.service enable: true ` store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: testProfileIgnitionYAML}, IgnitionConfigs: map[string]string{testProfileIgnitionYAML.IgnitionId: content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.ignitionHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Ignition template is rendered with Group metadata and selectors // - Rendered Ignition template is parsed as YAML // - Ignition Config served as JSON assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType)) assert.Equal(t, expectedIgnitionV2, w.Body.String()) }
func TestMetadataHandler_MetadataEdgeCases(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.metadataHandler() // groups with different metadata cases := []struct { group *storagepb.Group expected string }{ {&storagepb.Group{Metadata: []byte(`{"num":3}`)}, "NUM=3\n"}, {&storagepb.Group{Metadata: []byte(`{"yes":true}`)}, "YES=true\n"}, {&storagepb.Group{Metadata: []byte(`{"no":false}`)}, "NO=false\n"}, // Issue #84 - improve list and map printouts {&storagepb.Group{Metadata: []byte(`{"list":["3","d"]}`)}, "LIST=[3 d]\n"}, } for _, c := range cases { ctx := withGroup(context.Background(), c.group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - the Group's custom metadata is served // - key names are upper case assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, c.expected, w.Body.String()) assert.Equal(t, plainContentType, w.HeaderMap.Get(contentType)) } }
func TestRenderJSON_WriteError(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) w := NewUnwriteableResponseWriter() srv.renderJSON(w, map[string]string{"a": "b"}) assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Empty(t, w.Body.String()) }
func TestRenderJSON_EncodeError(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) w := httptest.NewRecorder() // channels cannot be JSON encoded srv.renderJSON(w, make(chan struct{})) assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Empty(t, w.Body.String()) }
func TestMetadataHandler_MissingCtxGroup(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.metadataHandler() w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, http.StatusNotFound, w.Code) }
func TestIgnitionHandler_MissingCtxProfile(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: &fake.EmptyStore{}}) h := srv.ignitionHandler(c) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, http.StatusNotFound, w.Code) }
func TestPixiecoreHandler_NoMatchingGroup(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: &fake.EmptyStore{}}) h := srv.pixiecoreHandler(c) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/"+validMACStr, nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, http.StatusNotFound, w.Code) }
func TestIPXEHandler_RenderTemplateError(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.ipxeHandler() // a Profile with nil NetBoot forces a template.Execute error ctx := withProfile(context.Background(), &storagepb.Profile{Boot: nil}) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) assert.Equal(t, http.StatusNotFound, w.Code) }
func TestIPXEHandler_WriteError(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.ipxeHandler() ctx := withProfile(context.Background(), fake.Profile) w := NewUnwriteableResponseWriter() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Empty(t, w.Body.String()) }
func TestPixiecoreHandler_InvalidMACAddress(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: &fake.EmptyStore{}}) h := srv.pixiecoreHandler(c) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, http.StatusBadRequest, w.Code) assert.Equal(t, "invalid MAC address /\n", w.Body.String()) }
func TestRenderJSON(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) w := httptest.NewRecorder() data := map[string][]string{ "a": []string{"b", "c"}, } srv.renderJSON(w, data) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType)) assert.Equal(t, `{"a":["b","c"]}`, w.Body.String()) }
func TestPixiecoreHandler_NoMatchingProfile(t *testing.T) { store := &fake.FixedStore{ Groups: map[string]*storagepb.Group{fake.Group.Id: fake.Group}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.pixiecoreHandler(c) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/"+validMACStr, nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, http.StatusNotFound, w.Code) }
func TestLoggingNormal(t *testing.T) { request, _ := http.NewRequest("GET", "robot.txt", nil) logger, hook := test.NewNullLogger() next := SingleFile("robot.txt") w := httptest.NewRecorder() Logging(next, logger).ServeHTTP(w, request) if len(hook.Entries) != 1 { t.Fatalf("Must return 1 but : %v", len(hook.Entries)) } }
func TestIPXEHandler(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.ipxeHandler() ctx := withProfile(context.Background(), fake.Profile) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - the Profile's NetBoot config is rendered as an iPXE script expectedScript := `#!ipxe kernel /image/kernel a=b c initrd /image/initrd_a /image/initrd_b boot ` assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, expectedScript, w.Body.String()) }
func TestPixiecoreHandler(t *testing.T) { store := &fake.FixedStore{ Groups: map[string]*storagepb.Group{testGroupWithMAC.Id: testGroupWithMAC}, Profiles: map[string]*storagepb.Profile{testGroupWithMAC.Profile: fake.Profile}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.pixiecoreHandler(c) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/"+validMACStr, nil) h.ServeHTTP(context.Background(), w, req) // assert that: // - MAC address parameter is used for Group matching // - the Profile's NetBoot config is rendered as Pixiecore JSON expectedJSON := `{"kernel":"/image/kernel","initrd":["/image/initrd_a","/image/initrd_b"],"cmdline":{"a":"b","c":""}}` assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType)) assert.Equal(t, expectedJSON, w.Body.String()) }
func TestGenericHandler_MissingTemplateMetadata(t *testing.T) { content := `#foo-bar-baz template KEY={{.missing_key}} ` store := &fake.FixedStore{ Profiles: map[string]*storagepb.Profile{fake.Group.Profile: fake.Profile}, GenericConfigs: map[string]string{fake.Profile.GenericId: content}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) h := srv.cloudHandler(c) ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(ctx, w, req) // assert that: // - Generic template rendering errors because "missing_key" is not // present in the Group metadata assert.Equal(t, http.StatusNotFound, w.Code) }
func TestMetadataHandler(t *testing.T) { logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) h := srv.metadataHandler() ctx := withGroup(context.Background(), fake.Group) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/?uuid=a1b2c3d4", nil) h.ServeHTTP(ctx, w, req) // assert that: // - the Group's custom metadata and selectors are served // - key names are upper case expectedData := map[string]string{ "POD_NETWORK": "10.2.0.0/16", "SERVICE_NAME": "etcd2", "UUID": "a1b2c3d4", } assert.Equal(t, http.StatusOK, w.Code) // convert response (random order) to map (tests compare in order) assert.Equal(t, expectedData, metadataToMap(w.Body.String())) assert.Equal(t, plainContentType, w.HeaderMap.Get(contentType)) }
func TestSelectGroup(t *testing.T) { store := &fake.FixedStore{ Groups: map[string]*storagepb.Group{fake.Group.Id: fake.Group}, } logger, _ := logtest.NewNullLogger() srv := NewServer(&Config{Logger: logger}) c := server.NewServer(&server.Config{Store: store}) next := func(ctx context.Context, w http.ResponseWriter, req *http.Request) { group, err := groupFromContext(ctx) assert.Nil(t, err) assert.Equal(t, fake.Group, group) fmt.Fprintf(w, "next handler called") } // assert that: // - query params are used to match uuid=a1b2c3d4 to fake.Group // - the fake.Group is added to the context // - next handler is called h := srv.selectGroup(c, ContextHandlerFunc(next)) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "?uuid=a1b2c3d4", nil) h.ServeHTTP(context.Background(), w, req) assert.Equal(t, "next handler called", w.Body.String()) }
func TestLabelsFromRequest(t *testing.T) { emptyMap := map[string]string{} logger, _ := logtest.NewNullLogger() cases := []struct { urlString string labels map[string]string }{ {"http://a.io", emptyMap}, {"http://a.io?uuid=a1b2c3", map[string]string{"uuid": "a1b2c3"}}, {"http://a.io?uuid=a1b2c3", map[string]string{"uuid": "a1b2c3"}}, {"http://a.io?mac=52:da:00:89:d8:10", map[string]string{"mac": validMACStr}}, {"http://a.io?mac=52-da-00-89-d8-10", map[string]string{"mac": validMACStr}}, {"http://a.io?uuid=a1b2c3&mac=52:da:00:89:d8:10", map[string]string{"uuid": "a1b2c3", "mac": validMACStr}}, // parse and set MAC regardless of query argument case {"http://a.io?UUID=a1b2c3&MAC=52:DA:00:89:d8:10", map[string]string{"UUID": "a1b2c3", "MAC": validMACStr}}, // ignore MAC addresses which do not parse {"http://a.io?mac=x:x:x:x:x:x", emptyMap}, } for _, c := range cases { req, err := http.NewRequest("GET", c.urlString, nil) assert.Nil(t, err) assert.Equal(t, c.labels, labelsFromRequest(logger, req)) } }