Skip to content
Snippets Groups Projects
Commit 03c7396d authored by Alex Clemmer's avatar Alex Clemmer Committed by GitHub
Browse files

Merge pull request #18 from jessicayuen/env-dir

Environment base.libsonnet and overrides
parents 2169ce8b 6378e75a
No related branches found
No related tags found
No related merge requests found
...@@ -59,8 +59,6 @@ const ( ...@@ -59,8 +59,6 @@ const (
// environment or the -f flag. // environment or the -f flag.
flagFile = "file" flagFile = "file"
flagFileShort = "f" flagFileShort = "f"
componentsExtCodeKey = "__ksonnet/components"
) )
var clientConfig clientcmd.ClientConfig var clientConfig clientcmd.ClientConfig
...@@ -372,16 +370,17 @@ func expandEnvCmdObjs(cmd *cobra.Command, envSpec *envSpec, cwd metadata.AbsPath ...@@ -372,16 +370,17 @@ func expandEnvCmdObjs(cmd *cobra.Command, envSpec *envSpec, cwd metadata.AbsPath
return nil, err return nil, err
} }
libPath, envLibPath := manager.LibPaths(*envSpec.env) libPath, envLibPath, envComponentPath := manager.LibPaths(*envSpec.env)
expander.FlagJpath = append([]string{string(libPath), string(envLibPath)}, expander.FlagJpath...) expander.FlagJpath = append([]string{string(libPath), string(envLibPath)}, expander.FlagJpath...)
if !filesPresent { if !filesPresent {
fileNames, err = manager.ComponentPaths() componentPaths, err := manager.ComponentPaths()
if err != nil { if err != nil {
return nil, err return nil, err
} }
baseObjExtCode := fmt.Sprintf("%s=%s", componentsExtCodeKey, constructBaseObj(fileNames)) baseObjExtCode := fmt.Sprintf("%s=%s", metadata.ComponentsExtCodeKey, constructBaseObj(componentPaths))
expander.ExtCodes = append([]string{baseObjExtCode}) expander.ExtCodes = append([]string{baseObjExtCode}, expander.ExtCodes...)
fileNames = []string{string(envComponentPath)}
} }
} }
......
...@@ -16,9 +16,11 @@ ...@@ -16,9 +16,11 @@
package metadata package metadata
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
...@@ -30,7 +32,8 @@ import ( ...@@ -30,7 +32,8 @@ import (
) )
const ( const (
defaultEnvName = "default" defaultEnvName = "default"
metadataDirName = ".metadata"
schemaFilename = "swagger.json" schemaFilename = "swagger.json"
extensionsLibFilename = "k.libsonnet" extensionsLibFilename = "k.libsonnet"
...@@ -87,11 +90,17 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD ...@@ -87,11 +90,17 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
return err return err
} }
metadataPath := appendToAbsPath(envPath, metadataDirName)
err = m.appFS.MkdirAll(string(metadataPath), defaultFolderPermissions)
if err != nil {
return err
}
log.Infof("Generating environment metadata at path '%s'", envPath) log.Infof("Generating environment metadata at path '%s'", envPath)
// Generate the schema file. // Generate the schema file.
log.Debugf("Generating '%s', length: %d", schemaFilename, len(specData)) log.Debugf("Generating '%s', length: %d", schemaFilename, len(specData))
schemaPath := appendToAbsPath(envPath, schemaFilename) schemaPath := appendToAbsPath(metadataPath, schemaFilename)
err = afero.WriteFile(m.appFS, string(schemaPath), specData, defaultFilePermissions) err = afero.WriteFile(m.appFS, string(schemaPath), specData, defaultFilePermissions)
if err != nil { if err != nil {
log.Debugf("Failed to write '%s'", schemaFilename) log.Debugf("Failed to write '%s'", schemaFilename)
...@@ -99,7 +108,7 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD ...@@ -99,7 +108,7 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
} }
log.Debugf("Generating '%s', length: %d", k8sLibFilename, len(k8sLibData)) log.Debugf("Generating '%s', length: %d", k8sLibFilename, len(k8sLibData))
k8sLibPath := appendToAbsPath(envPath, k8sLibFilename) k8sLibPath := appendToAbsPath(metadataPath, k8sLibFilename)
err = afero.WriteFile(m.appFS, string(k8sLibPath), k8sLibData, defaultFilePermissions) err = afero.WriteFile(m.appFS, string(k8sLibPath), k8sLibData, defaultFilePermissions)
if err != nil { if err != nil {
log.Debugf("Failed to write '%s'", k8sLibFilename) log.Debugf("Failed to write '%s'", k8sLibFilename)
...@@ -107,13 +116,24 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD ...@@ -107,13 +116,24 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
} }
log.Debugf("Generating '%s', length: %d", extensionsLibFilename, len(extensionsLibData)) log.Debugf("Generating '%s', length: %d", extensionsLibFilename, len(extensionsLibData))
extensionsLibPath := appendToAbsPath(envPath, extensionsLibFilename) extensionsLibPath := appendToAbsPath(metadataPath, extensionsLibFilename)
err = afero.WriteFile(m.appFS, string(extensionsLibPath), extensionsLibData, defaultFilePermissions) err = afero.WriteFile(m.appFS, string(extensionsLibPath), extensionsLibData, defaultFilePermissions)
if err != nil { if err != nil {
log.Debugf("Failed to write '%s'", extensionsLibFilename) log.Debugf("Failed to write '%s'", extensionsLibFilename)
return err return err
} }
// Generate the environment .jsonnet file
overrideFileName := path.Base(name) + ".jsonnet"
overrideData := m.generateOverrideData()
log.Debugf("Generating '%s', length: %d", overrideFileName, len(overrideData))
overrideLibPath := appendToAbsPath(envPath, overrideFileName)
err = afero.WriteFile(m.appFS, string(overrideLibPath), overrideData, defaultFilePermissions)
if err != nil {
log.Debugf("Failed to write '%s'", overrideFileName)
return err
}
// Generate the environment spec file. // Generate the environment spec file.
envSpecData, err := generateSpecData(uri) envSpecData, err := generateSpecData(uri)
if err != nil { if err != nil {
...@@ -327,6 +347,17 @@ func (m *manager) generateKsonnetLibData(spec ClusterSpec) ([]byte, []byte, []by ...@@ -327,6 +347,17 @@ func (m *manager) generateKsonnetLibData(spec ClusterSpec) ([]byte, []byte, []by
return extensionsLibData, k8sLibData, text, err return extensionsLibData, k8sLibData, text, err
} }
func (m *manager) generateOverrideData() []byte {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("local base = import \"%s\";\n", m.baseLibsonnetPath))
buf.WriteString(fmt.Sprintf("local k = import \"%s\";\n\n", extensionsLibFilename))
buf.WriteString("base + {\n")
buf.WriteString(" // Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
buf.WriteString(" // \"nginx-deployment\"+: k.deployment.mixin.metadata.labels({foo: \"bar\"})\n")
buf.WriteString("}\n")
return buf.Bytes()
}
func generateSpecData(uri string) ([]byte, error) { func generateSpecData(uri string) ([]byte, error) {
// Format the spec json and return; preface keys with 2 space idents. // Format the spec json and return; preface keys with 2 space idents.
return json.MarshalIndent(EnvironmentSpec{URI: uri}, "", " ") return json.MarshalIndent(EnvironmentSpec{URI: uri}, "", " ")
......
...@@ -180,3 +180,21 @@ func TestSetEnvironment(t *testing.T) { ...@@ -180,3 +180,21 @@ func TestSetEnvironment(t *testing.T) {
t.Fatalf("Expected set URI to be \"%s\", got:\n %s", set.URI, envSpec.URI) t.Fatalf("Expected set URI to be \"%s\", got:\n %s", set.URI, envSpec.URI)
} }
} }
func TestGenerateOverrideData(t *testing.T) {
m := mockEnvironments(t, "test-gen-override-data")
expected := `local base = import "test-gen-override-data/environments/base.libsonnet";
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:
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
}
`
result := m.generateOverrideData()
if string(result) != expected {
t.Fatalf("Expected to generate override file with data:\n%s\n,got:\n%s", expected, result)
}
}
...@@ -43,7 +43,7 @@ type Manager interface { ...@@ -43,7 +43,7 @@ type Manager interface {
Root() AbsPath Root() AbsPath
ComponentPaths() (AbsPaths, error) ComponentPaths() (AbsPaths, error)
CreateComponent(name string, text string, templateType prototype.TemplateType) error CreateComponent(name string, text string, templateType prototype.TemplateType) error
LibPaths(envName string) (libPath, envLibPath AbsPath) LibPaths(envName string) (libPath, envLibPath, envComponentPath AbsPath)
CreateEnvironment(name, uri string, spec ClusterSpec) error CreateEnvironment(name, uri string, spec ClusterSpec) error
DeleteEnvironment(name string) error DeleteEnvironment(name string) error
GetEnvironments() ([]*Environment, error) GetEnvironments() ([]*Environment, error)
......
...@@ -38,6 +38,11 @@ const ( ...@@ -38,6 +38,11 @@ const (
componentsDir = "components" componentsDir = "components"
environmentsDir = "environments" environmentsDir = "environments"
vendorDir = "vendor" vendorDir = "vendor"
baseLibsonnetFile = "base.libsonnet"
// ComponentsExtCodeKey is the ExtCode key for component imports
ComponentsExtCodeKey = "__ksonnet/components"
) )
type manager struct { type manager struct {
...@@ -49,6 +54,8 @@ type manager struct { ...@@ -49,6 +54,8 @@ type manager struct {
componentsPath AbsPath componentsPath AbsPath
environmentsPath AbsPath environmentsPath AbsPath
vendorDir AbsPath vendorDir AbsPath
baseLibsonnetPath AbsPath
} }
func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) { func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) {
...@@ -115,6 +122,8 @@ func newManager(rootPath AbsPath, appFS afero.Fs) *manager { ...@@ -115,6 +122,8 @@ func newManager(rootPath AbsPath, appFS afero.Fs) *manager {
componentsPath: appendToAbsPath(rootPath, componentsDir), componentsPath: appendToAbsPath(rootPath, componentsDir),
environmentsPath: appendToAbsPath(rootPath, environmentsDir), environmentsPath: appendToAbsPath(rootPath, environmentsDir),
vendorDir: appendToAbsPath(rootPath, vendorDir), vendorDir: appendToAbsPath(rootPath, vendorDir),
baseLibsonnetPath: appendToAbsPath(rootPath, environmentsDir, baseLibsonnetFile),
} }
} }
...@@ -169,8 +178,9 @@ func (m *manager) CreateComponent(name string, text string, templateType prototy ...@@ -169,8 +178,9 @@ func (m *manager) CreateComponent(name string, text string, templateType prototy
return afero.WriteFile(m.appFS, componentPath, []byte(text), defaultFilePermissions) return afero.WriteFile(m.appFS, componentPath, []byte(text), defaultFilePermissions)
} }
func (m *manager) LibPaths(envName string) (libPath, envLibPath AbsPath) { func (m *manager) LibPaths(envName string) (libPath, envLibPath, envComponentPath AbsPath) {
return m.libPath, appendToAbsPath(m.environmentsPath, envName) envPath := appendToAbsPath(m.environmentsPath, envName)
return m.libPath, appendToAbsPath(envPath, metadataDirName), appendToAbsPath(envPath, path.Base(envName)+".jsonnet")
} }
func (m *manager) createAppDirTree() error { func (m *manager) createAppDirTree() error {
...@@ -181,7 +191,7 @@ func (m *manager) createAppDirTree() error { ...@@ -181,7 +191,7 @@ func (m *manager) createAppDirTree() error {
return fmt.Errorf("Could not create app; directory '%s' already exists", m.rootPath) return fmt.Errorf("Could not create app; directory '%s' already exists", m.rootPath)
} }
paths := []AbsPath{ dirPaths := []AbsPath{
m.rootPath, m.rootPath,
m.ksonnetPath, m.ksonnetPath,
m.libPath, m.libPath,
...@@ -190,11 +200,19 @@ func (m *manager) createAppDirTree() error { ...@@ -190,11 +200,19 @@ func (m *manager) createAppDirTree() error {
m.vendorDir, m.vendorDir,
} }
for _, p := range paths { for _, p := range dirPaths {
if err := m.appFS.MkdirAll(string(p), defaultFolderPermissions); err != nil { if err := m.appFS.MkdirAll(string(p), defaultFolderPermissions); err != nil {
return err return err
} }
} }
return nil return afero.WriteFile(m.appFS, string(m.baseLibsonnetPath), genBaseLibsonnetContent(), defaultFilePermissions)
}
func genBaseLibsonnetContent() []byte {
return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `");
components + {
// Insert user-specified overrides here.
}
`)
} }
...@@ -17,6 +17,7 @@ package metadata ...@@ -17,6 +17,7 @@ package metadata
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"sort" "sort"
"testing" "testing"
...@@ -84,8 +85,10 @@ func TestInitSuccess(t *testing.T) { ...@@ -84,8 +85,10 @@ func TestInitSuccess(t *testing.T) {
} }
} }
envPath := appendToAbsPath(appPath, string(defaultEnvDir)) envPath := appendToAbsPath(appPath, string(environmentsDir))
schemaPath := appendToAbsPath(envPath, schemaFilename) metadataPath := appendToAbsPath(appPath, string(defaultEnvDir), string(metadataDirName))
schemaPath := appendToAbsPath(metadataPath, schemaFilename)
bytes, err := afero.ReadFile(testFS, string(schemaPath)) bytes, err := afero.ReadFile(testFS, string(schemaPath))
if err != nil { if err != nil {
t.Fatalf("Failed to read swagger file at '%s':\n%v", schemaPath, err) t.Fatalf("Failed to read swagger file at '%s':\n%v", schemaPath, err)
...@@ -93,7 +96,7 @@ func TestInitSuccess(t *testing.T) { ...@@ -93,7 +96,7 @@ func TestInitSuccess(t *testing.T) {
t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger) t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger)
} }
k8sLibPath := appendToAbsPath(envPath, k8sLibFilename) k8sLibPath := appendToAbsPath(metadataPath, k8sLibFilename)
k8sLibBytes, err := afero.ReadFile(testFS, string(k8sLibPath)) k8sLibBytes, err := afero.ReadFile(testFS, string(k8sLibPath))
if err != nil { if err != nil {
t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", k8sLibPath, err) t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", k8sLibPath, err)
...@@ -101,13 +104,21 @@ func TestInitSuccess(t *testing.T) { ...@@ -101,13 +104,21 @@ func TestInitSuccess(t *testing.T) {
t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", k8sLibPath, blankK8sLib, actualK8sLib) t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", k8sLibPath, blankK8sLib, actualK8sLib)
} }
extensionsLibPath := appendToAbsPath(envPath, extensionsLibFilename) extensionsLibPath := appendToAbsPath(metadataPath, extensionsLibFilename)
extensionsLibBytes, err := afero.ReadFile(testFS, string(extensionsLibPath)) extensionsLibBytes, err := afero.ReadFile(testFS, string(extensionsLibPath))
if err != nil { if err != nil {
t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", extensionsLibPath, err) t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", extensionsLibPath, err)
} else if string(extensionsLibBytes) == "" { } else if string(extensionsLibBytes) == "" {
t.Fatalf("Expected extension library file at '%s' to be non-empty", extensionsLibPath) t.Fatalf("Expected extension library file at '%s' to be non-empty", extensionsLibPath)
} }
baseLibsonnetPath := appendToAbsPath(envPath, baseLibsonnetFile)
baseLibsonnetBytes, err := afero.ReadFile(testFS, string(baseLibsonnetPath))
if err != nil {
t.Fatalf("Failed to read base.libsonnet file at '%s':\n%v", baseLibsonnetPath, err)
} else if len(baseLibsonnetBytes) == 0 {
t.Fatalf("Expected base.libsonnet at '%s' to be non-empty", baseLibsonnetPath)
}
} }
func TestFindSuccess(t *testing.T) { func TestFindSuccess(t *testing.T) {
...@@ -200,6 +211,25 @@ func TestComponentPaths(t *testing.T) { ...@@ -200,6 +211,25 @@ func TestComponentPaths(t *testing.T) {
} }
} }
func TestLibPaths(t *testing.T) {
appName := "test-lib-paths"
expectedLibPath := path.Join(appName, libDir)
expectedEnvLibPath := path.Join(appName, environmentsDir, mockEnvName, metadataDirName)
expectedEnvComponentPath := path.Join(appName, environmentsDir, mockEnvName, path.Base(mockEnvName)+".jsonnet")
m := mockEnvironments(t, appName)
libPath, envLibPath, envComponentPath := m.LibPaths(mockEnvName)
if string(libPath) != expectedLibPath {
t.Fatalf("Expected lib path to be:\n '%s'\n, got:\n '%s'", expectedLibPath, libPath)
}
if string(envLibPath) != expectedEnvLibPath {
t.Fatalf("Expected environment lib path to be:\n '%s'\n, got:\n '%s'", expectedEnvLibPath, envLibPath)
}
if string(envComponentPath) != expectedEnvComponentPath {
t.Fatalf("Expected environment component path to be:\n '%s'\n, got:\n '%s'", expectedEnvComponentPath, envComponentPath)
}
}
func TestFindFailure(t *testing.T) { func TestFindFailure(t *testing.T) {
findFailure := func(t *testing.T, currDir AbsPath) { findFailure := func(t *testing.T, currDir AbsPath) {
_, err := findManager(currDir, testFS) _, err := findManager(currDir, testFS)
......
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