Skip to content
Snippets Groups Projects
Commit 4dd0dd7a authored by Alex Clemmer's avatar Alex Clemmer
Browse files

Emit `app.yaml` after init

When the user calls `ks init <whatever>`, we need to emit an `app.yaml`
for the new project. This commit will introduce such behavior.
parent 78bfa356
No related branches found
No related tags found
No related merge requests found
...@@ -70,7 +70,7 @@ var initCmd = &cobra.Command{ ...@@ -70,7 +70,7 @@ var initCmd = &cobra.Command{
return err return err
} }
c, err := kubecfg.NewInitCmd(appRoot, specFlag, &uri, &namespace) c, err := kubecfg.NewInitCmd(appName, appRoot, specFlag, &uri, &namespace)
if err != nil { if err != nil {
return err return err
} }
...@@ -112,7 +112,7 @@ consists of two steps: ...@@ -112,7 +112,7 @@ consists of two steps:
# Initialize ksonnet application, using an OpenAPI specification file # Initialize ksonnet application, using an OpenAPI specification file
# generated by a build of Kubernetes to generate 'ksonnet-lib'. # generated by a build of Kubernetes to generate 'ksonnet-lib'.
ks init app-name --api-spec=file:swagger.json ks init app-name --api-spec=file:swagger.json
# Initialize ksonnet application, using the context 'dev' from the kubeconfig # Initialize ksonnet application, using the context 'dev' from the kubeconfig
# file. # file.
ks init app-name --context=dev`, ks init app-name --context=dev`,
......
...@@ -46,7 +46,7 @@ func mockEnvironments(t *testing.T, appName string) *manager { ...@@ -46,7 +46,7 @@ func mockEnvironments(t *testing.T, appName string) *manager {
} }
appPath := AbsPath(appName) appPath := AbsPath(appName)
m, err := initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) m, err := initManager(appName, appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err != nil { if err != nil {
t.Fatalf("Failed to init cluster spec: %v", err) t.Fatalf("Failed to init cluster spec: %v", err)
} }
......
...@@ -78,8 +78,8 @@ func Find(path AbsPath) (Manager, error) { ...@@ -78,8 +78,8 @@ func Find(path AbsPath) (Manager, error) {
// Init will retrieve a cluster API specification, generate a // Init will retrieve a cluster API specification, generate a
// capabilities-compliant version of ksonnet-lib, and then generate the // capabilities-compliant version of ksonnet-lib, and then generate the
// directory tree for an application. // directory tree for an application.
func Init(rootPath AbsPath, spec ClusterSpec, serverURI, namespace *string) (Manager, error) { func Init(name string, rootPath AbsPath, spec ClusterSpec, serverURI, namespace *string) (Manager, error) {
return initManager(rootPath, spec, serverURI, namespace, appFS) return initManager(name, rootPath, spec, serverURI, namespace, appFS)
} }
// ClusterSpec represents the API supported by some cluster. There are several // ClusterSpec represents the API supported by some cluster. There are several
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package metadata package metadata
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"os/user" "os/user"
...@@ -23,6 +24,7 @@ import ( ...@@ -23,6 +24,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/ksonnet/ksonnet/metadata/app"
param "github.com/ksonnet/ksonnet/metadata/params" param "github.com/ksonnet/ksonnet/metadata/params"
"github.com/ksonnet/ksonnet/prototype" "github.com/ksonnet/ksonnet/prototype"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
...@@ -43,6 +45,7 @@ const ( ...@@ -43,6 +45,7 @@ const (
componentParamsFile = "params.libsonnet" componentParamsFile = "params.libsonnet"
baseLibsonnetFile = "base.libsonnet" baseLibsonnetFile = "base.libsonnet"
appYAMLFile = "app.yaml"
// ComponentsExtCodeKey is the ExtCode key for component imports // ComponentsExtCodeKey is the ExtCode key for component imports
ComponentsExtCodeKey = "__ksonnet/components" ComponentsExtCodeKey = "__ksonnet/components"
...@@ -63,10 +66,11 @@ type manager struct { ...@@ -63,10 +66,11 @@ type manager struct {
libPath AbsPath libPath AbsPath
componentsPath AbsPath componentsPath AbsPath
environmentsPath AbsPath environmentsPath AbsPath
vendorDir AbsPath vendorPath AbsPath
componentParamsPath AbsPath componentParamsPath AbsPath
baseLibsonnetPath AbsPath baseLibsonnetPath AbsPath
appYAMLPath AbsPath
// User-level paths. // User-level paths.
userKsonnetRootPath AbsPath userKsonnetRootPath AbsPath
...@@ -95,7 +99,7 @@ func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) { ...@@ -95,7 +99,7 @@ func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) {
} }
} }
func initManager(rootPath AbsPath, spec ClusterSpec, serverURI, namespace *string, appFS afero.Fs) (*manager, error) { func initManager(name string, rootPath AbsPath, spec ClusterSpec, serverURI, namespace *string, appFS afero.Fs) (*manager, error) {
m, err := newManager(rootPath, appFS) m, err := newManager(rootPath, appFS)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -114,7 +118,7 @@ func initManager(rootPath AbsPath, spec ClusterSpec, serverURI, namespace *strin ...@@ -114,7 +118,7 @@ func initManager(rootPath AbsPath, spec ClusterSpec, serverURI, namespace *strin
} }
// Initialize directory structure. // Initialize directory structure.
if err := m.createAppDirTree(); err != nil { if err := m.createAppDirTree(name); err != nil {
return nil, err return nil, err
} }
...@@ -150,10 +154,11 @@ func newManager(rootPath AbsPath, appFS afero.Fs) (*manager, error) { ...@@ -150,10 +154,11 @@ func newManager(rootPath AbsPath, appFS afero.Fs) (*manager, error) {
libPath: appendToAbsPath(rootPath, libDir), libPath: appendToAbsPath(rootPath, libDir),
componentsPath: appendToAbsPath(rootPath, componentsDir), componentsPath: appendToAbsPath(rootPath, componentsDir),
environmentsPath: appendToAbsPath(rootPath, environmentsDir), environmentsPath: appendToAbsPath(rootPath, environmentsDir),
vendorDir: appendToAbsPath(rootPath, vendorDir), vendorPath: appendToAbsPath(rootPath, vendorDir),
componentParamsPath: appendToAbsPath(rootPath, componentsDir, componentParamsFile), componentParamsPath: appendToAbsPath(rootPath, componentsDir, componentParamsFile),
baseLibsonnetPath: appendToAbsPath(rootPath, environmentsDir, baseLibsonnetFile), baseLibsonnetPath: appendToAbsPath(rootPath, environmentsDir, baseLibsonnetFile),
appYAMLPath: appendToAbsPath(rootPath, appYAMLFile),
// User-level paths. // User-level paths.
userKsonnetRootPath: userRootPath, userKsonnetRootPath: userRootPath,
...@@ -270,7 +275,7 @@ func (m *manager) createUserDirTree() error { ...@@ -270,7 +275,7 @@ func (m *manager) createUserDirTree() error {
return nil return nil
} }
func (m *manager) createAppDirTree() error { func (m *manager) createAppDirTree(name string) error {
exists, err := afero.DirExists(m.appFS, string(m.rootPath)) exists, err := afero.DirExists(m.appFS, string(m.rootPath))
if err != nil { if err != nil {
return fmt.Errorf("Could not check existance of directory '%s':\n%v", m.rootPath, err) return fmt.Errorf("Could not check existance of directory '%s':\n%v", m.rootPath, err)
...@@ -284,7 +289,7 @@ func (m *manager) createAppDirTree() error { ...@@ -284,7 +289,7 @@ func (m *manager) createAppDirTree() error {
m.libPath, m.libPath,
m.componentsPath, m.componentsPath,
m.environmentsPath, m.environmentsPath,
m.vendorDir, m.vendorPath,
} }
for _, p := range dirPaths { for _, p := range dirPaths {
...@@ -293,6 +298,11 @@ func (m *manager) createAppDirTree() error { ...@@ -293,6 +298,11 @@ func (m *manager) createAppDirTree() error {
} }
} }
appYAML, err := genAppYAMLContent(name)
if err != nil {
return err
}
filePaths := []struct { filePaths := []struct {
path AbsPath path AbsPath
content []byte content []byte
...@@ -305,6 +315,10 @@ func (m *manager) createAppDirTree() error { ...@@ -305,6 +315,10 @@ func (m *manager) createAppDirTree() error {
m.baseLibsonnetPath, m.baseLibsonnetPath,
genBaseLibsonnetContent(), genBaseLibsonnetContent(),
}, },
{
m.appYAMLPath,
appYAML,
},
} }
for _, f := range filePaths { for _, f := range filePaths {
...@@ -344,6 +358,17 @@ func genComponentParamsContent() []byte { ...@@ -344,6 +358,17 @@ func genComponentParamsContent() []byte {
`) `)
} }
func genAppYAMLContent(name string) ([]byte, error) {
content := app.Spec{
APIVersion: app.DefaultAPIVersion,
Kind: app.Kind,
Name: name,
Version: app.DefaultVersion,
}
return json.MarshalIndent(&content, "", " ")
}
func genBaseLibsonnetContent() []byte { func genBaseLibsonnetContent() []byte {
return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `"); return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `");
components + { components + {
......
...@@ -61,7 +61,7 @@ func TestInitSuccess(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestInitSuccess(t *testing.T) {
} }
appPath := AbsPath("/fromEmptySwagger") appPath := AbsPath("/fromEmptySwagger")
_, err = initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) _, err = initManager("fromEmptySwagger", appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err != nil { if err != nil {
t.Fatalf("Failed to init cluster spec: %v", err) t.Fatalf("Failed to init cluster spec: %v", err)
} }
...@@ -150,6 +150,14 @@ func TestInitSuccess(t *testing.T) { ...@@ -150,6 +150,14 @@ func TestInitSuccess(t *testing.T) {
} else if len(baseLibsonnetBytes) == 0 { } else if len(baseLibsonnetBytes) == 0 {
t.Fatalf("Expected base.libsonnet at '%s' to be non-empty", baseLibsonnetPath) t.Fatalf("Expected base.libsonnet at '%s' to be non-empty", baseLibsonnetPath)
} }
appYAMLPath := appendToAbsPath(appPath, appYAMLFile)
appYAMLBytes, err := afero.ReadFile(testFS, string(appYAMLPath))
if err != nil {
t.Fatalf("Failed to read app.yaml file at '%s':\n%v", appYAMLPath, err)
} else if len(appYAMLBytes) == 0 {
t.Fatalf("Expected app.yaml at '%s' to be non-empty", appYAMLPath)
}
} }
func TestFindSuccess(t *testing.T) { func TestFindSuccess(t *testing.T) {
...@@ -168,7 +176,7 @@ func TestFindSuccess(t *testing.T) { ...@@ -168,7 +176,7 @@ func TestFindSuccess(t *testing.T) {
} }
appPath := AbsPath("/findSuccess") appPath := AbsPath("/findSuccess")
_, err = initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) _, err = initManager("findSuccess", appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err != nil { if err != nil {
t.Fatalf("Failed to init cluster spec: %v", err) t.Fatalf("Failed to init cluster spec: %v", err)
} }
...@@ -196,7 +204,7 @@ func TestComponentPaths(t *testing.T) { ...@@ -196,7 +204,7 @@ func TestComponentPaths(t *testing.T) {
} }
appPath := AbsPath("/componentPaths") appPath := AbsPath("/componentPaths")
m, err := initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) m, err := initManager("componentPaths", appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err != nil { if err != nil {
t.Fatalf("Failed to init cluster spec: %v", err) t.Fatalf("Failed to init cluster spec: %v", err)
} }
...@@ -286,13 +294,13 @@ func TestDoubleNewFailure(t *testing.T) { ...@@ -286,13 +294,13 @@ func TestDoubleNewFailure(t *testing.T) {
appPath := AbsPath("/doubleNew") appPath := AbsPath("/doubleNew")
_, err = initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) _, err = initManager("doubleNew", appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err != nil { if err != nil {
t.Fatalf("Failed to init cluster spec: %v", err) t.Fatalf("Failed to init cluster spec: %v", err)
} }
targetErr := fmt.Sprintf("Could not create app; directory '%s' already exists", appPath) targetErr := fmt.Sprintf("Could not create app; directory '%s' already exists", appPath)
_, err = initManager(appPath, spec, &mockAPIServer, &mockNamespace, testFS) _, err = initManager("doubleNew", appPath, spec, &mockAPIServer, &mockNamespace, testFS)
if err == nil || err.Error() != targetErr { if err == nil || err.Error() != targetErr {
t.Fatalf("Expected to fail to create app with message '%s', got '%s'", targetErr, err.Error()) t.Fatalf("Expected to fail to create app with message '%s', got '%s'", targetErr, err.Error())
} }
......
...@@ -3,13 +3,14 @@ package kubecfg ...@@ -3,13 +3,14 @@ package kubecfg
import "github.com/ksonnet/ksonnet/metadata" import "github.com/ksonnet/ksonnet/metadata"
type InitCmd struct { type InitCmd struct {
name string
rootPath metadata.AbsPath rootPath metadata.AbsPath
spec metadata.ClusterSpec spec metadata.ClusterSpec
serverURI *string serverURI *string
namespace *string namespace *string
} }
func NewInitCmd(rootPath metadata.AbsPath, specFlag string, serverURI, namespace *string) (*InitCmd, error) { func NewInitCmd(name string, rootPath metadata.AbsPath, specFlag string, serverURI, namespace *string) (*InitCmd, error) {
// NOTE: We're taking `rootPath` here as an absolute path (rather than a partial path we expand to an absolute path) // NOTE: We're taking `rootPath` here as an absolute path (rather than a partial path we expand to an absolute path)
// to make it more testable. // to make it more testable.
...@@ -18,10 +19,10 @@ func NewInitCmd(rootPath metadata.AbsPath, specFlag string, serverURI, namespace ...@@ -18,10 +19,10 @@ func NewInitCmd(rootPath metadata.AbsPath, specFlag string, serverURI, namespace
return nil, err return nil, err
} }
return &InitCmd{rootPath: rootPath, spec: spec, serverURI: serverURI, namespace: namespace}, nil return &InitCmd{name: name, rootPath: rootPath, spec: spec, serverURI: serverURI, namespace: namespace}, nil
} }
func (c *InitCmd) Run() error { func (c *InitCmd) Run() error {
_, err := metadata.Init(c.rootPath, c.spec, c.serverURI, c.namespace) _, err := metadata.Init(c.name, c.rootPath, c.spec, c.serverURI, c.namespace)
return err return err
} }
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