diff --git a/metadata/manager.go b/metadata/manager.go index 9898bfed3b3ffbcfec433cbc953632b745270570..7f60687810cb3ba2a42362a68b6d75724e6c230e 100644 --- a/metadata/manager.go +++ b/metadata/manager.go @@ -1,11 +1,14 @@ package metadata import ( + "encoding/json" "fmt" "os" "path" "path/filepath" + "github.com/ksonnet/ksonnet-lib/ksonnet-gen/ksonnet" + "github.com/ksonnet/ksonnet-lib/ksonnet-gen/kubespec" "github.com/spf13/afero" ) @@ -24,7 +27,8 @@ const ( schemaDir = "vendor/schema" vendorLibDir = "vendor/lib" - schemaFilename = "swagger.json" + schemaFilename = "swagger.json" + ksonnetLibCoreFilename = "k8s.libsonnet" ) type manager struct { @@ -62,18 +66,41 @@ func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) { } func initManager(rootPath AbsPath, spec ClusterSpec, appFS afero.Fs) (*manager, error) { - data, err := spec.data() + // + // IMPLEMENTATION NOTE: We get the cluster specification and generate + // ksonnet-lib before initializing the directory structure so that failure of + // either (e.g., GET'ing the spec from a live cluster returns 404) does not + // result in a partially-initialized directory structure. + // + + // Get cluster specification data, possibly from the network. + specData, err := spec.data() if err != nil { return nil, err } m := newManager(rootPath, appFS) + // Generate the program text for ksonnet-lib. + ksonnetLibDir := appendToAbsPath(m.schemaDir, defaultEnvName) + ksonnetLibData, err := generateKsonnetLibData(ksonnetLibDir, specData) + if err != nil { + return nil, err + } + + // Initialize directory structure. if err = m.createAppDirTree(); err != nil { return nil, err } - if err = m.cacheClusterSpecData(defaultEnvName, data); err != nil { + // Cache specification data. + if err = m.cacheClusterSpecData(defaultEnvName, specData); err != nil { + return nil, err + } + + ksonnetLibPath := appendToAbsPath(ksonnetLibDir, ksonnetLibCoreFilename) + err = afero.WriteFile(appFS, string(ksonnetLibPath), ksonnetLibData, 0644) + if err != nil { return nil, err } @@ -136,3 +163,18 @@ func (m *manager) createAppDirTree() error { return nil } + +func generateKsonnetLibData(ksonnetLibDir AbsPath, text []byte) ([]byte, error) { + // Deserialize the API object. + s := kubespec.APISpec{} + err := json.Unmarshal(text, &s) + if err != nil { + return nil, err + } + + s.Text = text + s.FilePath = filepath.Dir(string(ksonnetLibDir)) + + // Emit Jsonnet code. + return ksonnet.Emit(&s, nil, nil) +} diff --git a/metadata/manager_test.go b/metadata/manager_test.go index 1329bc8b9d5385d6f5f9d8227bba8a96c540304f..d73fd97cc8c4f1e8bb332a3d35c9d40e680b70ee 100644 --- a/metadata/manager_test.go +++ b/metadata/manager_test.go @@ -10,7 +10,25 @@ import ( const ( blankSwagger = "/blankSwagger.json" - blankSwaggerData = `{}` + blankSwaggerData = `{ + "swagger": "2.0", + "info": { + "title": "Kubernetes", + "version": "v1.7.0" + }, + "paths": { + }, + "definitions": { + } +}` + blankKsonnetLib = `// AUTOGENERATED from the Kubernetes OpenAPI specification. DO NOT MODIFY. +// Kubernetes version: v1.7.0 + +{ + local hidden = { + }, +} +` ) var appFS = afero.NewMemMapFs() @@ -60,6 +78,14 @@ func TestInitSuccess(t *testing.T) { } else if actualSwagger := string(bytes); actualSwagger != blankSwaggerData { t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger) } + + ksonnetLibPath := appendToAbsPath(envPath, ksonnetLibCoreFilename) + ksonnetLibBytes, err := afero.ReadFile(appFS, string(ksonnetLibPath)) + if err != nil { + t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", ksonnetLibPath, err) + } else if actualKsonnetLib := string(ksonnetLibBytes); actualKsonnetLib != blankKsonnetLib { + t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", ksonnetLibPath, blankKsonnetLib, actualKsonnetLib) + } } func TestFindSuccess(t *testing.T) {