Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
prometheus-operator: Add conversion tests
Browse files Browse the repository at this point in the history
- This commit adds new conversion test to verify if the external_url
  works fine.

- This commit adds test cases in TestRenderManifest to verify if the
  prometheus external_url and prometheus ingress host matches.

Signed-off-by: Suraj Deshmukh <suraj@kinvolk.io>
  • Loading branch information
surajssd committed Oct 15, 2020
1 parent 2539257 commit 79255db
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 0 deletions.
61 changes: 61 additions & 0 deletions pkg/components/internal/testutil/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2020 The Lokomotive Authors
//
// 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 testutil

import (
"testing"

"github.com/hashicorp/hcl/v2"

"github.com/kinvolk/lokomotive/pkg/components"
"github.com/kinvolk/lokomotive/pkg/components/util"
)

// ConfigFromMap takes a map and a key and returns the value associated with the key. If the key
// does not exists in that map it fails.
func ConfigFromMap(t *testing.T, m map[string]string, k string) string {
ret, ok := m[k]
if !ok {
t.Fatalf("Config not found with filename: %q", k)
}

return ret
}

// RenderManifests converts a component into YAML manifests.
func RenderManifests(
t *testing.T, component components.Component, componentName string, hclConfig string,
) map[string]string {
body, diagnostics := util.GetComponentBody(hclConfig, componentName)
if diagnostics.HasErrors() {
t.Fatalf("Getting component body: %v", diagnostics.Errs())
}

diagnostics = component.LoadConfig(body, &hcl.EvalContext{})
if diagnostics.HasErrors() {
t.Fatalf("Loading configuration: %v", diagnostics)
}

ret, err := component.RenderManifests()
if err != nil {
t.Fatalf("Rendering manifests: %v", err)
}

if len(ret) == 0 {
t.Fatalf("Rendered manifests shouldn't be empty")
}

return ret
}
16 changes: 16 additions & 0 deletions pkg/components/internal/testutil/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2020 The Lokomotive Authors
//
// 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 testutil contains helper functions for component unit tests.
package testutil
91 changes: 91 additions & 0 deletions pkg/components/internal/testutil/jsonpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2020 The Lokomotive Authors
//
// 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 testutil

import (
"reflect"
"testing"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/client-go/util/jsonpath"
)

// unstructredObj accepts a Kubernetes manifest in YAML format and returns an object of type
// `unstructured.Unstructured`. This object has many methods that can be used by the consumer to
// extract metadata from the Kubernetes manifest.
func unstructredObj(t *testing.T, yamlObj string) *unstructured.Unstructured {
u := &unstructured.Unstructured{}

// Decode YAML into `unstructured.Unstructured`.
dec := yamlserializer.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
if _, _, err := dec.Decode([]byte(yamlObj), nil, u); err != nil {
t.Fatalf("Converting config to unstructured.Unstructured: %v", err)
}

return u
}

// valFromObject takes a JSON path as a string and an object of type `unstructured.Unstructured`.
// This function returns an object of type `reflect.Value` at that JSON path.
func valFromObject(t *testing.T, jp string, obj *unstructured.Unstructured) reflect.Value {
jPath := jsonpath.New("parse")
if err := jPath.Parse(jp); err != nil {
t.Fatalf("Parsing JSONPath: %v", err)
}

v, err := jPath.FindResults(obj.Object)
if err != nil {
t.Fatalf("Finding results using JSONPath in the YAML file: %v", err)
}

if len(v) == 0 || len(v[0]) == 0 {
t.Fatalf("No result found")
}

return v[0][0]
}

// jsonPathValue extracts an object at a JSON path from a YAML config, and returns an interface
// object.
func jsonPathValue(t *testing.T, yamlConfig string, jsonPath string) interface{} {
u := unstructredObj(t, yamlConfig)
got := valFromObject(t, jsonPath, u)

switch got.Kind() { //nolint:exhaustive
case reflect.Interface:
// TODO: Add type switch here for concrete types.
return got.Interface()
default:
t.Fatalf("Extracted object has an unknown type: %v", got.Kind())
}

return nil
}

// MatchJSONPathStringValue is a helper function for component unit tests. It compares the string at
// a JSON path in a YAML config to the expected string.
func MatchJSONPathStringValue(t *testing.T, yamlConfig string, jsonPath string, expected string) {
obj := jsonPathValue(t, yamlConfig, jsonPath)

got, ok := obj.(string)
if !ok {
t.Fatalf("Value is not string: %#v", obj)
}

if got != expected {
t.Fatalf("Expected: %s, Got: %s", expected, got)
}
}
101 changes: 101 additions & 0 deletions pkg/components/prometheus-operator/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package prometheus
import (
"testing"

"github.com/kinvolk/lokomotive/pkg/components/internal/testutil"
"github.com/kinvolk/lokomotive/pkg/components/util"
)

Expand Down Expand Up @@ -62,6 +63,34 @@ component "prometheus-operator" {
}`,
wantErr: true,
},
{
desc: "prometheus ingress and external_url given and are different",
hcl: `
component "prometheus-operator" {
prometheus {
external_url = "https://prometheus.notmydomain.net"
ingress {
host = "prometheus.mydomain.net"
}
}
}
`,
wantErr: true,
},
{
desc: "prometheus ingress and external_url given and are same",
hcl: `
component "prometheus-operator" {
prometheus {
external_url = "https://prometheus.mydomain.net"
ingress {
host = "prometheus.mydomain.net"
}
}
}
`,
wantErr: false,
},
}

for _, tc := range tests {
Expand Down Expand Up @@ -98,3 +127,75 @@ component "prometheus-operator" {
})
}
}

//nolint:funlen
func TestConversion(t *testing.T) {
testCases := []struct {
name string
inputConfig string
expectedManifestName string
expected string
jsonPath string
}{
{
name: "use external_url param",
inputConfig: `
component "prometheus-operator" {
prometheus {
external_url = "https://prometheus.externalurl.net"
}
}
`,
expectedManifestName: "prometheus-operator/templates/prometheus/prometheus.yaml",
expected: "https://prometheus.externalurl.net",
jsonPath: "{.spec.externalUrl}",
},
{
name: "no external_url param",
inputConfig: `
component "prometheus-operator" {
prometheus {
ingress {
host = "prometheus.mydomain.net"
class = "contour"
certmanager_cluster_issuer = "letsencrypt-production"
}
}
}
`,
expectedManifestName: "prometheus-operator/templates/prometheus/prometheus.yaml",
expected: "https://prometheus.mydomain.net",
jsonPath: "{.spec.externalUrl}",
},
{
name: "ingress creation for prometheus",
inputConfig: `
component "prometheus-operator" {
prometheus {
ingress {
host = "prometheus.mydomain.net"
class = "contour"
certmanager_cluster_issuer = "letsencrypt-production"
}
}
}
`,
expectedManifestName: "prometheus-operator/templates/prometheus/ingress.yaml",
expected: "prometheus.mydomain.net",
jsonPath: "{.spec.rules[0].host}",
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

component := newComponent()
m := testutil.RenderManifests(t, component, name, tc.inputConfig)
gotConfig := testutil.ConfigFromMap(t, m, tc.expectedManifestName)

testutil.MatchJSONPathStringValue(t, gotConfig, tc.jsonPath, tc.expected)
})
}
}
46 changes: 46 additions & 0 deletions vendor/k8s.io/apimachinery/pkg/runtime/serializer/yaml/yaml.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ k8s.io/apimachinery/pkg/runtime/serializer/protobuf
k8s.io/apimachinery/pkg/runtime/serializer/recognizer
k8s.io/apimachinery/pkg/runtime/serializer/streaming
k8s.io/apimachinery/pkg/runtime/serializer/versioning
k8s.io/apimachinery/pkg/runtime/serializer/yaml
k8s.io/apimachinery/pkg/selection
k8s.io/apimachinery/pkg/types
k8s.io/apimachinery/pkg/util/cache
Expand Down

0 comments on commit 79255db

Please sign in to comment.