diff --git a/commands/operator-sdk/cmd/build.go b/commands/operator-sdk/cmd/build.go index e72a5ebca8..7b10b8479a 100644 --- a/commands/operator-sdk/cmd/build.go +++ b/commands/operator-sdk/cmd/build.go @@ -2,10 +2,14 @@ package cmd import ( "fmt" + "io/ioutil" "os" "os/exec" + "github.com/coreos/operator-sdk/pkg/generator" + "github.com/spf13/cobra" + yaml "gopkg.in/yaml.v2" ) func NewBuildCmd() *cobra.Command { @@ -31,6 +35,7 @@ For example: const ( build = "./tmp/build/build.sh" dockerBuild = "./tmp/build/docker_build.sh" + configYaml = "./config/config.yaml" ) func buildFunc(cmd *cobra.Command, args []string) { @@ -53,5 +58,16 @@ func buildFunc(cmd *cobra.Command, args []string) { ExitWithError(ExitError, fmt.Errorf("failed to output build image %v: (%v)", image, err)) } fmt.Fprintln(os.Stdout, string(o)) - // TODO: generates Kubernetes manifests + + c := &generator.Config{} + fp, err := ioutil.ReadFile(configYaml) + if err != nil { + ExitWithError(ExitError, fmt.Errorf("failed to read config file %v: (%v)", configYaml, err)) + } + if err = yaml.Unmarshal(fp, c); err != nil { + ExitWithError(ExitError, fmt.Errorf("failed to unmarshal config file %v: (%v)", configYaml, err)) + } + if err = generator.RenderDeployFiles(c, image); err != nil { + ExitWithError(ExitError, fmt.Errorf("failed to generate deploy/operator.yaml: (%v)", err)) + } } diff --git a/pkg/generator/deploy_tmpl.go b/pkg/generator/deploy_tmpl.go new file mode 100644 index 0000000000..deae1490c5 --- /dev/null +++ b/pkg/generator/deploy_tmpl.go @@ -0,0 +1,33 @@ +package generator + +const operatorYamlTmpl = `apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: {{.KindPlural}}.{{.GroupName}} +spec: + group: {{.GroupName}} + names: + kind: {{.Kind}} + listKind: {{.Kind}}List + plural: {{.KindPlural}} + singular: {{.KindSingular}} + scope: Namespaced + version: {{.Version}} +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{.ProjectName}} +spec: + replicas: 1 + template: + metadata: + labels: + name: {{.ProjectName}} + spec: + containers: + - name: {{.ProjectName}} + image: {{.Image}} + command: + - {{.ProjectName}} +` diff --git a/pkg/generator/gen_deploy.go b/pkg/generator/gen_deploy.go new file mode 100644 index 0000000000..6b0a5363b3 --- /dev/null +++ b/pkg/generator/gen_deploy.go @@ -0,0 +1,47 @@ +package generator + +import ( + "fmt" + "io" + "strings" + "text/template" +) + +const ( + operatorTmplName = "deploy/operator.yaml" +) + +// OperatorYaml contains all the customized data needed to generate deploy/operator.yaml for a new operator +// when pairing with operatorYamlTmpl template. +type OperatorYaml struct { + Kind string + KindSingular string + KindPlural string + GroupName string + Version string + ProjectName string + Image string +} + +// renderOperatorYaml generates deploy/operator.yaml. +func renderOperatorYaml(w io.Writer, kind, apiVersion, projectName, image string) error { + t := template.New(operatorTmplName) + t, err := t.Parse(operatorYamlTmpl) + if err != nil { + return fmt.Errorf("failed to parse operator yaml template: %v", err) + } + + ks := strings.ToLower(kind) + o := OperatorYaml{ + Kind: kind, + KindSingular: ks, + // suffix KindSingular with "s" to create KindPlural. + // TODO: make this more grammatically correct for special nouns. + KindPlural: ks + "s", + GroupName: groupName(apiVersion), + Version: version(apiVersion), + ProjectName: projectName, + Image: image, + } + return t.Execute(w, o) +} diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index c95d6cb7da..a52da0e421 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -37,6 +37,7 @@ const ( gopkgtoml = "Gopkg.toml" gopkglock = "Gopkg.lock" config = "config.yaml" + operatorYaml = deployDir + "/operator.yaml" ) type Generator struct { @@ -149,6 +150,15 @@ func (g *Generator) renderDeploy() error { return nil } +// RenderDeployFiles generates "deploy/operator.yaml". +func RenderDeployFiles(c *Config, image string) error { + buf := &bytes.Buffer{} + if err := renderOperatorYaml(buf, c.Kind, c.APIVersion, c.ProjectName, image); err != nil { + return err + } + return ioutil.WriteFile(operatorYaml, buf.Bytes(), defaultFileMode) +} + func (g *Generator) renderTmp() error { bDir := filepath.Join(g.projectName, buildDir) if err := os.MkdirAll(bDir, defaultDirFileMode); err != nil { diff --git a/pkg/generator/generator_test.go b/pkg/generator/generator_test.go index 3fd29516d4..5a78178d1e 100644 --- a/pkg/generator/generator_test.go +++ b/pkg/generator/generator_test.go @@ -289,3 +289,45 @@ func TestGenConfig(t *testing.T) { t.Errorf("want %v, got %v", configExp, buf.String()) } } + +const operatorYamlExp = `apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: appservices.app.example.com +spec: + group: app.example.com + names: + kind: AppService + listKind: AppServiceList + plural: appservices + singular: appservice + scope: Namespaced + version: v1alpha1 +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: app-operator +spec: + replicas: 1 + template: + metadata: + labels: + name: app-operator + spec: + containers: + - name: app-operator + image: quay.io/coreos/operator-sdk-dev:app-operator + command: + - app-operator +` + +func TestGenDeploy(t *testing.T) { + buf := &bytes.Buffer{} + if err := renderOperatorYaml(buf, "AppService", "app.example.com/v1alpha1", "app-operator", "quay.io/coreos/operator-sdk-dev:app-operator"); err != nil { + t.Error(err) + } + if operatorYamlExp != buf.String() { + t.Errorf("want %v, got %v", operatorYamlExp, buf.String()) + } +}