Unverified Commit 8eafea56 authored by Bryan Liles's avatar Bryan Liles Committed by GitHub
Browse files

Merge pull request #547 from bryanl/component-return-array

Convert component returned arrays into Kubernetes lists
parents bbe8021f 7de07c13
......@@ -84,17 +84,21 @@ func (s *Show) showYAML(apiObjects []*unstructured.Unstructured) error {
func (s *Show) showJSON(apiObjects []*unstructured.Unstructured) error {
enc := json.NewEncoder(s.Out)
enc.SetIndent("", " ")
m := map[string]interface{}{
"apiVersion": "v1",
"kind": "List",
}
items := make([]interface{}, 0)
for _, obj := range apiObjects {
// TODO: this is not valid framing for JSON
if len(apiObjects) > 1 {
fmt.Fprintln(s.Out, "---")
}
if err := enc.Encode(obj); err != nil {
return err
}
items = append(items, obj.Object)
}
return nil
m["items"] = items
return enc.Encode(m)
}
func ShowYAML(out io.Writer, apiObjects []*unstructured.Unstructured) error {
......
......@@ -57,7 +57,7 @@ func TestShow(t *testing.T) {
{
name: "show json",
format: "json",
expected: "---\n{\n \"kind\": \"a\"\n}\n---\n{\n \"kind\": \"b\"\n}\n",
expected: "{\n \"apiVersion\": \"v1\",\n \"items\": [\n {\n \"kind\": \"a\"\n },\n {\n \"kind\": \"b\"\n }\n ],\n \"kind\": \"List\"\n}\n",
findObjects: dummyObjects,
},
{
......
......@@ -94,17 +94,27 @@ func MainFile(a app.App, envName string) (string, error) {
// Evaluate evaluates an environment.
func Evaluate(a app.App, envName, components, paramsStr string) (string, error) {
libPath, err := a.LibPath(envName)
snippet, err := MainFile(a, envName)
if err != nil {
return "", err
}
appEnv, err := a.Environment(envName)
evaluated, err := evaluateMain(a, envName, snippet, components, paramsStr)
if err != nil {
return "", err
}
snippet, err := MainFile(a, envName)
return upgradeArray(evaluated)
}
func evaluateMain(a app.App, envName, snippet, components, paramsStr string) (string, error) {
libPath, err := a.LibPath(envName)
if err != nil {
return "", err
}
appEnv, err := a.Environment(envName)
if err != nil {
return "", err
}
......@@ -147,6 +157,27 @@ func Evaluate(a app.App, envName, components, paramsStr string) (string, error)
return vm.EvaluateSnippet(envFileName, snippet)
}
// upgradeArray wraps component lists in Kubernetes lists.
func upgradeArray(snippet string) (string, error) {
vm := jsonnet.NewVM()
vm.ExtCode("__src", snippet)
return vm.EvaluateSnippet("upgradeArray", jsonnetUpgradeArray)
}
var jsonnetUpgradeArray = `
local __src = std.extVar("__src");
local components = std.objectFields(__src);
{
[x]:
if std.type(__src[x]) == "array"
then {apiVersion: "v1", kind: "List", items: __src[x]}
else __src[x]
for x in components
}
`
func envRoot(a app.App, envName string) (string, error) {
envSpec, err := a.Environment(envName)
if err != nil {
......
......@@ -16,12 +16,14 @@
package env
import (
"io/ioutil"
"path/filepath"
"testing"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/app/mocks"
"github.com/ksonnet/ksonnet/pkg/util/test"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
)
......@@ -203,3 +205,50 @@ func withJsonnetPaths(fn func()) {
fn()
}
func TestEvaluate(t *testing.T) {
test.WithApp(t, "/app", func(a *mocks.App, fs afero.Fs) {
envSpec := &app.EnvironmentSpec{
Path: "default",
Destination: &app.EnvironmentDestinationSpec{
Server: "http://example.com",
Namespace: "default",
},
}
a.On("Environment", "default").Return(envSpec, nil)
test.StageFile(t, fs, "main.jsonnet", "/app/environments/default/main.jsonnet")
components, err := ioutil.ReadFile(filepath.FromSlash("testdata/evaluate/components.jsonnet"))
require.NoError(t, err)
got, err := Evaluate(a, "default", string(components), "")
require.NoError(t, err)
test.AssertOutput(t, "evaluate/out.jsonnet", got)
})
}
func TestMainFile(t *testing.T) {
test.WithApp(t, "/app", func(a *mocks.App, fs afero.Fs) {
envSpec := &app.EnvironmentSpec{}
a.On("Environment", "default").Return(envSpec, nil)
test.StageFile(t, fs, "main.jsonnet", "/app/environments/main.jsonnet")
got, err := MainFile(a, "default")
require.NoError(t, err)
test.AssertOutput(t, "main.jsonnet", got)
})
}
func Test_upgradeArray(t *testing.T) {
snippet, err := ioutil.ReadFile(filepath.FromSlash("testdata/upgradeArray/in.jsonnet"))
require.NoError(t, err)
got, err := upgradeArray(string(snippet))
require.NoError(t, err)
test.AssertOutput(t, "upgradeArray/out.jsonnet", got)
}
{
"guestbook-ui": [
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"ports": [
{
"port": 80,
"targetPort": 80
}
],
"selector": {
"app": "guestbook-ui"
},
"type": "NodePort"
}
},
{
"apiVersion": "apps/v1beta1",
"kind": "Deployment",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"replicas": 1,
"template": {
"metadata": {
"labels": {
"app": "guestbook-ui"
}
},
"spec": {
"containers": [
{
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
]
}
\ No newline at end of file
// main.jsonnet
\ No newline at end of file
local base = {};
// uncomment if you reference ksonnet-lib
// local k = import "k.libsonnet";
base + {
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
}
\ No newline at end of file
{
"guestbook-ui": [
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"ports": [
{
"port": 80,
"targetPort": 80
}
],
"selector": {
"app": "guestbook-ui"
},
"type": "NodePort"
}
},
{
"apiVersion": "apps/v1beta1",
"kind": "Deployment",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"replicas": 1,
"template": {
"metadata": {
"labels": {
"app": "guestbook-ui"
}
},
"spec": {
"containers": [
{
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
]
}
\ No newline at end of file
{
"guestbook-ui": {
"apiVersion": "v1",
"items": [
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"ports": [
{
"port": 80,
"targetPort": 80
}
],
"selector": {
"app": "guestbook-ui"
},
"type": "NodePort"
}
},
{
"apiVersion": "apps/v1beta1",
"kind": "Deployment",
"metadata": {
"name": "guestbook-ui"
},
"spec": {
"replicas": 1,
"template": {
"metadata": {
"labels": {
"app": "guestbook-ui"
}
},
"spec": {
"containers": [
{
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
],
"kind": "List"
}
}
......@@ -16,12 +16,10 @@
package params
import (
"encoding/json"
"path/filepath"
"github.com/ksonnet/ksonnet/pkg/app"
"github.com/ksonnet/ksonnet/pkg/util/jsonnet"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
)
......@@ -47,45 +45,3 @@ func EvaluateEnv(a app.App, sourcePath, paramsStr, envName string) (string, erro
return vm.EvaluateSnippet(sourcePath, string(snippet))
}
// EvaluateComponentSnippet evaluates a component with jsonnet using a snippet.
func EvaluateComponentSnippet(a app.App, snippet, paramsStr, envName string, useMemoryImporter bool) (string, error) {
logrus.WithFields(logrus.Fields{
"env-name": envName,
}).Debug("evaluate component params")
libPath, err := a.LibPath(envName)
if err != nil {
return "", err
}
vm := jsonnet.NewVM()
if useMemoryImporter {
vm.Fs = a.Fs()
vm.UseMemoryImporter = true
}
vm.AddJPath(
libPath,
filepath.Join(a.Root(), "vendor"),
)
vm.ExtCode("__ksonnet/params", paramsStr)
envDetails, err := a.Environment(envName)
if err != nil {
return "", err
}
dest := map[string]string{
"server": envDetails.Destination.Server,
"namespace": envDetails.Destination.Namespace,
}
marshalledDestination, err := json.Marshal(&dest)
if err != nil {
return "", err
}
vm.ExtCode("__ksonnet/environments", string(marshalledDestination))
return vm.EvaluateSnippet("snippet", snippet)
}
......@@ -208,11 +208,7 @@ func (p *Pipeline) moduleObjects(module component.Module, filter []string) ([]*u
switch componentType {
case "jsonnet":
patched, err = params.EvaluateComponentSnippet(p.app, string(data), envParamData, p.envName, false)
if err != nil {
return nil, errors.Wrap(err, "patch Jsonnet component")
}
patched = string(data)
case "yaml":
patched, err = params.PatchJSON(string(data), envParamData, k)
if err != nil {
......
......@@ -16,6 +16,7 @@
package test
import (
"bytes"
"io"
"io/ioutil"
"os"
......@@ -24,6 +25,7 @@ import (
"testing"
"github.com/ksonnet/ksonnet/pkg/app/mocks"
godiff "github.com/shazow/go-diff"
"github.com/spf13/afero"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
......@@ -113,9 +115,14 @@ func WithAppFs(t *testing.T, root string, fs afero.Fs, fn func(*mocks.App, afero
// AssertOutput asserts the output matches the actual contents
func AssertOutput(t *testing.T, filename, actual string) {
path := filepath.Join("testdata", filename)
b, err := ioutil.ReadFile(path)
path := filepath.Join("testdata", filepath.FromSlash(filename))
f, err := os.Open(path)
require.NoError(t, err)
require.Equal(t, string(b), actual)
rActual := strings.NewReader(actual)
var buf bytes.Buffer
err = godiff.DefaultDiffer().Diff(&buf, f, rActual)
require.NoError(t, err)
require.Empty(t, buf.String())
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment