blob: 32d84e706340ffad6e42b48ac914b726a5af7792 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package policymanagerutil
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// isValidMetadataRequest is a generic helper function to verify that the
// request matches the spec set by GCE metadata service and supports the
// behavior specified in the MetadataRetriever implementation.
func isValidMetadataRequest(r *http.Request) error {
// Check the request is a GET request.
if r.Method != "GET" {
return fmt.Errorf("got a %s request, want GET request",
r.Method)
}
// Check request contains the right headers.
if _, ok := r.Header[requestHeaderKey]; !ok {
return fmt.Errorf("missing %s header", requestHeaderKey)
}
return nil
}
// TestGetEntry tests that the getEntry function for the following things:
// - makes a GET request with the required headers
// - raises an error if the status code is not 200
// - returns the correct response from the server
// - returns the correct etag from the server
func TestGetEntry(t *testing.T) {
tests := []struct {
name string
entry string
expectResponse string
expectErr bool
expectEtag string
}{
{
"InstanceID",
instanceIDEndpoint,
"1234567890",
false,
"",
},
{
"InstanceCustomMetaData",
instanceCustomDataDirectory,
`{
"key1": "value1",
"key2": "value2"
}`,
false,
"myEtag",
},
{
"ServerError",
instanceIDEndpoint,
"",
true,
"",
},
}
for _, test := range tests {
// Set up a test server.
handler := func(w http.ResponseWriter, r *http.Request) {
if test.expectErr {
w.WriteHeader(http.StatusInternalServerError)
return
}
if err := isValidMetadataRequest(r); err != nil {
t.Errorf("Test %s: %v", test.name, err)
}
w.Header().Set("etag", test.expectEtag)
fmt.Fprint(w, test.expectResponse)
}
ts := httptest.NewServer(http.HandlerFunc(handler))
resp, etag, err := getEntry(test.entry, ts.URL)
if err != nil {
if !test.expectErr {
t.Errorf("Test %s got unexpected error %v",
test.name,
err)
}
} else {
if test.expectErr {
t.Errorf("Test %s got %s, want error",
test.name,
string(resp))
} else {
if string(resp) != test.expectResponse {
t.Errorf("Test %s got %s, want %s",
test.name,
string(resp),
test.expectResponse)
}
if etag != test.expectEtag {
t.Errorf("Test %s got etag %s, want %s",
test.name,
etag,
test.expectEtag)
}
}
}
ts.Close()
}
}
// TestGetInstanceID tests that the retriever implementation correctly fetches
// the instance id from the metadata server.
func TestGetInstanceID(t *testing.T) {
expectID := uint64(15134250476052400915)
// Set up a test server.
handler := func(w http.ResponseWriter, r *http.Request) {
if err := isValidMetadataRequest(r); err != nil {
t.Error(err)
}
// Make sure the request is for the right metadata entry.
if r.URL.Path != instanceIDEndpoint {
t.Errorf("got URL path %s, want %s",
r.URL.Path,
instanceIDEndpoint)
}
fmt.Fprintf(w, "%v", expectID)
}
ts := httptest.NewServer(http.HandlerFunc(handler))
defer ts.Close()
// Set up retriever to talk to the test server.
retriever := NewMetadataRetriever(ts.URL)
if id, err := retriever.GetInstanceID(); err != nil {
t.Error(err)
} else if id != expectID {
t.Errorf("got %d, want %d", id, expectID)
}
}
// TestFetchMetadata tests that the retriever implementation correctly fetches
// the content of instance custom metadata directory.
func TestFetchMetadata(t *testing.T) {
// Dummy key-value pairs.
dummyMap := map[string]string{
"key1": "val1",
"key2": "val2",
"key3": "\t val3 \n ",
}
tests := []struct {
name string
lastEtag string
expectResponse string
expectEtag string
expectErr bool
}{
{
"InstanceCustomMetadataWait",
"lastEtag",
dummyMap["key1"],
"myEtag1",
false,
},
{
"InstanceCustomMetadataNoWait",
"",
dummyMap["key2"],
"myEtag2",
false,
},
{
"InstanceCustomMetadataWhitespaces",
"",
dummyMap["key3"],
"myEtag3",
false,
},
{
"InstanceCustomDataServerError",
"",
"",
"",
true,
},
}
for _, test := range tests {
// Set up a test server.
handler := func(w http.ResponseWriter, r *http.Request) {
if test.expectErr {
w.WriteHeader(http.StatusInternalServerError)
return
}
if err := isValidMetadataRequest(r); err != nil {
t.Errorf("Test %s: %v", test.name, err)
}
// Make sure the request is for the directory.
if r.URL.Path != instanceCustomDataDirectory {
t.Errorf("Test %s got URL path %s, want %s",
test.name,
r.URL.Path,
instanceCustomDataDirectory)
}
expectedQuery := "recursive=true"
if test.lastEtag != "" {
expectedQuery += "&wait_for_change=true&last_etag=" + test.lastEtag
}
if r.URL.RawQuery != expectedQuery {
t.Errorf("Test %s got HTTP request query %s, want %s",
test.name,
r.URL.RawQuery,
expectedQuery)
}
w.Header().Set("etag", test.expectEtag)
fmt.Fprint(w, test.expectResponse)
}
ts := httptest.NewServer(http.HandlerFunc(handler))
// Create retriever to talk to the test server.
retriever := NewMetadataRetriever(ts.URL)
resp, etag, err := retriever.FetchMetadata(test.lastEtag)
if err != nil {
if !test.expectErr {
t.Errorf("Test %s got unexpected error %v",
test.name,
err)
}
} else {
if test.expectErr {
t.Errorf("Test %s got %s, want error",
test.name,
resp)
} else {
if resp != strings.TrimSpace(test.expectResponse) {
t.Errorf("Test %s got %v, want %v",
test.name,
resp,
test.expectResponse)
}
if etag != test.expectEtag {
t.Errorf("Test %s got etag %s, want %s",
test.name,
etag,
test.expectEtag)
}
}
}
ts.Close()
}
}