Skip to content
Snippets Groups Projects
Unverified Commit e109bc8b authored by Jess's avatar Jess Committed by GitHub
Browse files

Merge pull request #282 from jessicayuen/namespaces

Implement explicit env metadata
parents b4b09278 6120adfa
No related branches found
No related tags found
No related merge requests found
......@@ -315,7 +315,8 @@ func expandEnvObjs(cmd *cobra.Command, env string, manager metadata.Manager) ([]
return nil, err
}
libPath, vendorPath, envLibPath, envComponentPath, envParamsPath := manager.LibPaths(env)
libPath, vendorPath := manager.LibPaths()
metadataPath, mainPath, paramsPath, specPath := manager.EnvPaths(env)
componentPaths, err := manager.ComponentPaths()
if err != nil {
return nil, err
......@@ -325,12 +326,13 @@ func expandEnvObjs(cmd *cobra.Command, env string, manager metadata.Manager) ([]
if err != nil {
return nil, err
}
params := importParams(string(envParamsPath))
params := importParams(string(paramsPath))
spec := importEnv(string(specPath))
expander.FlagJpath = append([]string{string(libPath), string(vendorPath), string(envLibPath)}, expander.FlagJpath...)
expander.ExtCodes = append([]string{baseObj, params}, expander.ExtCodes...)
expander.FlagJpath = append([]string{string(libPath), string(vendorPath), string(metadataPath)}, expander.FlagJpath...)
expander.ExtCodes = append([]string{baseObj, params, spec}, expander.ExtCodes...)
envFiles := []string{string(envComponentPath)}
envFiles := []string{string(mainPath)}
return expander.Expand(envFiles)
}
......@@ -449,7 +449,7 @@ expand prototypes into Jsonnet files.
ks prototype use io.ksonnet.pkg.single-port-deployment nginx-depl \
--image=nginx
If the optional ` + "`--name`" + ` tag is not specified, all Kubernetes API resources
If the optional ` + "`--name`" + ` tag is not specified, all Kubernetes API resources
declared by this prototype use this argument as their own ` + "`metadata.name`" + `
3. Prototypes can be further customized by passing in **parameters** via additional
......@@ -503,7 +503,10 @@ func expandPrototype(proto *prototype.SpecificationSchema, templateType prototyp
if !utils.IsASCIIIdentifier(componentName) {
componentsText = fmt.Sprintf(`components["%s"]`, componentName)
}
template = append([]string{`local params = std.extVar("` + metadata.ParamsExtCodeKey + `").` + componentsText + ";"}, template...)
template = append([]string{
`local env = std.extVar("` + metadata.EnvExtCodeKey + `");`,
`local params = std.extVar("` + metadata.ParamsExtCodeKey + `").` + componentsText + ";"},
template...)
return jsonnet.Parse(componentName, strings.Join(template, "\n"))
}
......
......@@ -399,8 +399,10 @@ func expandEnvCmdObjs(cmd *cobra.Command, env string, components []string, cwd m
return nil, err
}
libPath, vendorPath, envLibPath, envComponentPath, envParamsPath := manager.LibPaths(env)
expander.FlagJpath = append([]string{string(libPath), string(vendorPath), string(envLibPath)}, expander.FlagJpath...)
libPath, vendorPath := manager.LibPaths()
metadataPath, mainPath, paramsPath, specPath := manager.EnvPaths(env)
expander.FlagJpath = append([]string{string(libPath), string(vendorPath), string(metadataPath)}, expander.FlagJpath...)
componentPaths, err := manager.ComponentPaths()
if err != nil {
......@@ -411,14 +413,20 @@ func expandEnvCmdObjs(cmd *cobra.Command, env string, components []string, cwd m
if err != nil {
return nil, err
}
params := importParams(string(envParamsPath))
expander.ExtCodes = append([]string{baseObj, params}, expander.ExtCodes...)
//
// Set up ExtCodes to resolve runtime variables such as the environment namespace.
//
params := importParams(string(paramsPath))
spec := importEnv(string(specPath))
expander.ExtCodes = append([]string{baseObj, params, spec}, expander.ExtCodes...)
//
// Expand the ksonnet app as rendered for environment `env`.
//
return expander.Expand([]string{string(envComponentPath)})
return expander.Expand([]string{string(mainPath)})
}
// constructBaseObj constructs the base Jsonnet object that represents k-v
......@@ -502,3 +510,7 @@ func constructBaseObj(componentPaths, componentNames []string) (string, error) {
func importParams(path string) string {
return fmt.Sprintf(`%s=import "%s"`, metadata.ParamsExtCodeKey, path)
}
func importEnv(path string) string {
return fmt.Sprintf(`%s=import "%s"`, metadata.EnvExtCodeKey, path)
}
......@@ -45,7 +45,8 @@ type AbsPaths []string
// libraries; and other non-core-application tasks.
type Manager interface {
Root() AbsPath
LibPaths(envName string) (libPath, vendorPath, envLibPath, envComponentPath, envParamsPath AbsPath)
LibPaths() (libPath, vendorPath AbsPath)
EnvPaths(env string) (metadataPath, mainPath, paramsPath, specPath AbsPath)
// Components API.
ComponentPaths() (AbsPaths, error)
......
......@@ -51,7 +51,9 @@ const (
// ComponentsExtCodeKey is the ExtCode key for component imports
ComponentsExtCodeKey = "__ksonnet/components"
// ParamsExtCodeKey is the ExtCode key for importing environment parameters
// EnvExtCodeKey is the ExtCode key for importing environment metadata
EnvExtCodeKey = "__ksonnet/environments"
// ParamsExtCodeKey is the ExtCode key for importing component parameters
ParamsExtCodeKey = "__ksonnet/params"
// User-level ksonnet directories.
......@@ -197,10 +199,23 @@ func (m *manager) Root() AbsPath {
return m.rootPath
}
func (m *manager) LibPaths(envName string) (libPath, vendorPath, envLibPath, envComponentPath, envParamsPath AbsPath) {
envPath := appendToAbsPath(m.environmentsPath, envName)
return m.libPath, m.vendorPath, appendToAbsPath(envPath, metadataDirName),
appendToAbsPath(envPath, envFileName), appendToAbsPath(envPath, componentParamsFile)
func (m *manager) LibPaths() (libPath, vendorPath AbsPath) {
return m.libPath, m.vendorPath
}
func (m *manager) EnvPaths(env string) (metadataPath, mainPath, paramsPath, specPath AbsPath) {
envPath := appendToAbsPath(m.environmentsPath, env)
// .metadata directory
metadataPath = appendToAbsPath(envPath, metadataDirName)
// main.jsonnet file
mainPath = appendToAbsPath(envPath, envFileName)
// params.libsonnet file
paramsPath = appendToAbsPath(envPath, componentParamsFile)
// spec.json file
specPath = appendToAbsPath(envPath, specFilename)
return
}
func (m *manager) GetComponentParams(component string) (param.Params, error) {
......
......@@ -210,26 +210,38 @@ func TestLibPaths(t *testing.T) {
appName := "test-lib-paths"
expectedVendorPath := path.Join(appName, vendorDir)
expectedLibPath := path.Join(appName, libDir)
expectedEnvLibPath := path.Join(appName, environmentsDir, mockEnvName, metadataDirName)
expectedEnvComponentPath := path.Join(appName, environmentsDir, mockEnvName, envFileName)
expectedEnvParamsPath := path.Join(appName, environmentsDir, mockEnvName, paramsFileName)
m := mockEnvironments(t, appName)
libPath, vendorPath, envLibPath, envComponentPath, envParamsPath := m.LibPaths(mockEnvName)
libPath, vendorPath := m.LibPaths()
if string(libPath) != expectedLibPath {
t.Fatalf("Expected lib path to be:\n '%s'\n, got:\n '%s'", expectedLibPath, libPath)
}
if string(vendorPath) != expectedVendorPath {
t.Fatalf("Expected vendor lib path to be:\n '%s'\n, got:\n '%s'", expectedVendorPath, vendorPath)
}
if string(envLibPath) != expectedEnvLibPath {
t.Fatalf("Expected environment lib path to be:\n '%s'\n, got:\n '%s'", expectedEnvLibPath, envLibPath)
}
func TestEnvPaths(t *testing.T) {
appName := "test-env-paths"
expectedMetadataPath := path.Join(appName, environmentsDir, mockEnvName, metadataDirName)
expectedMainPath := path.Join(appName, environmentsDir, mockEnvName, envFileName)
expectedParamsPath := path.Join(appName, environmentsDir, mockEnvName, paramsFileName)
expectedSpecPath := path.Join(appName, environmentsDir, mockEnvName, specFilename)
m := mockEnvironments(t, appName)
metadataPath, mainPath, paramsPath, specPath := m.EnvPaths(mockEnvName)
if string(metadataPath) != expectedMetadataPath {
t.Fatalf("Expected environment metadata dir path to be:\n '%s'\n, got:\n '%s'", expectedMetadataPath, metadataPath)
}
if string(mainPath) != expectedMainPath {
t.Fatalf("Expected environment main path to be:\n '%s'\n, got:\n '%s'", expectedMainPath, mainPath)
}
if string(envComponentPath) != expectedEnvComponentPath {
t.Fatalf("Expected environment component path to be:\n '%s'\n, got:\n '%s'", expectedEnvComponentPath, envComponentPath)
if string(paramsPath) != expectedParamsPath {
t.Fatalf("Expected environment params path to be:\n '%s'\n, got:\n '%s'", expectedParamsPath, paramsPath)
}
if string(envParamsPath) != expectedEnvParamsPath {
t.Fatalf("Expected environment params path to be:\n '%s'\n, got:\n '%s'", expectedEnvParamsPath, envParamsPath)
if string(specPath) != expectedSpecPath {
t.Fatalf("Expected environment spec path to be:\n '%s'\n, got:\n '%s'", expectedSpecPath, specPath)
}
}
......
......@@ -17,6 +17,7 @@ package jsonnet
import (
"errors"
"fmt"
"sort"
"strings"
......@@ -27,6 +28,9 @@ import (
const (
paramPrefix = "param://"
paramReplacementPrefix = "params."
envPrefix = "env://"
envReplacementPrefix = "env."
)
// Parse rewrites the imports in a Jsonnet file before returning the snippet.
......@@ -52,14 +56,14 @@ func parse(fn string, jsonnet string) (string, error) {
var imports []ast.Import
// Gather all parameter imports
// Gather all parameter or environment imports
err = visit(root, &imports)
if err != nil {
return "", err
}
// Replace all parameter imports
return replace(jsonnet, imports), nil
// Replace all parameter or environment imports
return replace(jsonnet, imports)
}
// ---------------------------------------------------------------------------
......@@ -67,11 +71,17 @@ func parse(fn string, jsonnet string) (string, error) {
func visit(node ast.Node, imports *[]ast.Import) error {
switch n := node.(type) {
case *ast.Import:
// Add parameter-type imports to the list of replacements.
// Add parameter/environment type imports to the list of replacements.
if strings.HasPrefix(n.File.Value, paramPrefix) {
param := strings.TrimPrefix(n.File.Value, paramPrefix)
if len(param) < 1 {
return errors.New("There must be a parameter following import param://")
return fmt.Errorf("There must be a parameter following import %s", paramPrefix)
}
*imports = append(*imports, *n)
} else if strings.HasPrefix(n.File.Value, envPrefix) {
env := strings.TrimPrefix(n.File.Value, envPrefix)
if len(env) < 1 {
return fmt.Errorf("There must be a attribute following import %s", envPrefix)
}
*imports = append(*imports, *n)
}
......@@ -297,8 +307,10 @@ func visitLocalBind(node ast.LocalBind, imports *[]ast.Import) error {
// ---------------------------------------------------------------------------
// replace converts all parameters in the passed Jsonnet of form
// `import 'param://port'` into `params.port`.
func replace(jsonnet string, imports []ast.Import) string {
//
// 1. `import 'param://port'` into `params.port`.
// 2. `import 'env://namespace'` into `env.namespace`.
func replace(jsonnet string, imports []ast.Import) (string, error) {
lines := strings.Split(jsonnet, "\n")
// Imports must be sorted by reverse location to avoid indexing problems
......@@ -311,28 +323,35 @@ func replace(jsonnet string, imports []ast.Import) string {
})
for _, im := range imports {
param := paramReplacementPrefix + strings.TrimPrefix(im.File.Value, paramPrefix)
var replacement string
if strings.HasPrefix(im.File.Value, paramPrefix) {
replacement = paramReplacementPrefix + strings.TrimPrefix(im.File.Value, paramPrefix)
} else if strings.HasPrefix(im.File.Value, envPrefix) {
replacement = envReplacementPrefix + strings.TrimPrefix(im.File.Value, envPrefix)
} else {
return "", fmt.Errorf("Found unsupported import prefix in %s", im.File.Value)
}
lineStart := im.Loc().Begin.Line
lineEnd := im.Loc().End.Line
colStart := im.Loc().Begin.Column
colEnd := im.Loc().End.Column
// Case where import param is split over multiple strings.
// Case where import is split over multiple strings.
if lineEnd != lineStart {
// Replace all intermediate lines with the empty string.
for i := lineStart; i < lineEnd-1; i++ {
lines[i] = ""
}
// Remove import param related logic from the last line.
// Remove import related logic from the last line.
lines[lineEnd-1] = lines[lineEnd-1][colEnd:len(lines[lineEnd-1])]
// Perform replacement in the first line of import param occurance.
lines[lineStart-1] = lines[lineStart-1][:colStart-1] + param
// Perform replacement in the first line of import occurance.
lines[lineStart-1] = lines[lineStart-1][:colStart-1] + replacement
} else {
line := lines[lineStart-1]
lines[lineStart-1] = line[:colStart-1] + param + line[colEnd:len(line)]
lines[lineStart-1] = line[:colStart-1] + replacement + line[colEnd:len(line)]
}
}
return strings.Join(lines, "\n")
return strings.Join(lines, "\n"), nil
}
......@@ -135,6 +135,32 @@ func TestParse(t *testing.T) {
`local f = f;
{ foo: f, }`,
},
// Test where there are multiple import types.
{
`
local k = import 'ksonnet.beta.2/k.libsonnet';
local service = k.core.v1.service;
local servicePort = k.core.v1.service.mixin.spec.portsType;
local port = servicePort.new((import 'param://port'), (import 'param://portName'));
local namespace = import 'env://namespace';
local name = import 'param://name';
k.core.v1.service.new('%s-service' % [name], {app: name}, port)`,
`
local k = import 'ksonnet.beta.2/k.libsonnet';
local service = k.core.v1.service;
local servicePort = k.core.v1.service.mixin.spec.portsType;
local port = servicePort.new((params.port), (params.portName));
local namespace = env.namespace;
local name = params.name;
k.core.v1.service.new('%s-service' % [name], {app: name}, port)`,
},
}
errors := []string{
......
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