Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion commands/operator-sdk/cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func buildFunc(cmd *cobra.Command, args []string) {
if err = yaml.Unmarshal(fp, c); err != nil {
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to unmarshal config file %v: (%v)", configYaml, err))
}
if err = generator.RenderDeployFiles(c, image); err != nil {
if err = generator.RenderOperatorYaml(c, image); err != nil {
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to generate deploy/operator.yaml: (%v)", err))
}
}
6 changes: 6 additions & 0 deletions pkg/generator/deploy_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ roleRef:
name: {{.ProjectName}}
apiGroup: rbac.authorization.k8s.io
`

const crYamlTmpl = `apiVersion: "{{.APIVersion}}"
kind: "{{.Kind}}"
metadata:
name: "example"
`
22 changes: 22 additions & 0 deletions pkg/generator/gen_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
const (
operatorTmplName = "deploy/operator.yaml"
rbacTmplName = "deploy/rbac.yaml"
crTmplName = "deploy/cr.yaml"
)

// OperatorYaml contains all the customized data needed to generate deploy/operator.yaml for a new operator
Expand Down Expand Up @@ -78,3 +79,24 @@ func renderRBACYaml(w io.Writer, projectName string) error {
r := RBACYaml{ProjectName: projectName}
return t.Execute(w, r)
}

// CRYaml contains all the customized data needed to generate deploy/cr.yaml.
type CRYaml struct {
APIVersion string
Kind string
Name string
}

func renderCustomResourceYaml(w io.Writer, apiVersion, kind string) error {
t := template.New(crTmplName)
t, err := t.Parse(crYamlTmpl)
if err != nil {
return fmt.Errorf("failed to parse cr yaml template: %v", err)
}

r := CRYaml{
APIVersion: apiVersion,
Kind: kind,
}
return t.Execute(w, r)
}
11 changes: 10 additions & 1 deletion pkg/generator/gen_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ import (
type Handler struct {
// imports
OperatorSDKImport string

RepoPath string
Kind string
APIDirName string
Version string
}

// renderHandlerFile generates the stub/handler.go file.
func renderHandlerFile(w io.Writer) error {
func renderHandlerFile(w io.Writer, repoPath, kind, apiDirName, version string) error {
t := template.New("stub/handler.go")
t, err := t.Parse(handlerTmpl)
if err != nil {
Expand All @@ -36,6 +41,10 @@ func renderHandlerFile(w io.Writer) error {

h := Handler{
OperatorSDKImport: sdkImport,
RepoPath: repoPath,
Kind: kind,
APIDirName: apiDirName,
Version: version,
}
return t.Execute(w, h)
}
9 changes: 7 additions & 2 deletions pkg/generator/gen_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ type Main struct {
OperatorSDKImport string
StubImport string
SDKVersionImport string

APIVersion string
Kind string
}

// renderMainFile generates the cmd/<projectName>/main.go file given a repo path ("github.com/coreos/play")
func renderMainFile(w io.Writer, repo string) error {
// renderMainFile generates the cmd/<projectName>/main.go file.
func renderMainFile(w io.Writer, repo, apiVersion, kind string) error {
t := template.New("cmd/<projectName>/main.go")
t, err := t.Parse(mainTmpl)
if err != nil {
Expand All @@ -47,6 +50,8 @@ func renderMainFile(w io.Writer, repo string) error {
OperatorSDKImport: sdkImport,
StubImport: filepath.Join(repo, stubDir),
SDKVersionImport: versionImport,
APIVersion: apiVersion,
Kind: kind,
}
return t.Execute(w, m)
}
39 changes: 28 additions & 11 deletions pkg/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
config = "config.yaml"
operatorYaml = deployDir + "/operator.yaml"
rbacYaml = "rbac.yaml"
crYaml = "cr.yaml"
)

type Generator struct {
Expand Down Expand Up @@ -127,12 +128,12 @@ func (g *Generator) renderCmd() error {
if err := os.MkdirAll(cpDir, defaultDirFileMode); err != nil {
return err
}
return renderCmdFiles(cpDir, g.repoPath)
return renderCmdFiles(cpDir, g.repoPath, g.apiVersion, g.kind)
}

func renderCmdFiles(cmdProjectDir, repoPath string) error {
func renderCmdFiles(cmdProjectDir, repoPath, apiVersion, kind string) error {
buf := &bytes.Buffer{}
if err := renderMainFile(buf, repoPath); err != nil {
if err := renderMainFile(buf, repoPath, apiVersion, kind); err != nil {
return err
}
return writeFileAndPrint(filepath.Join(cmdProjectDir, main), buf.Bytes(), defaultFileMode)
Expand All @@ -159,7 +160,7 @@ func (g *Generator) renderDeploy() error {
if err := os.MkdirAll(dp, defaultDirFileMode); err != nil {
return err
}
return renderRBAC(dp, g.projectName)
return renderDeployFiles(dp, g.projectName, g.apiVersion, g.kind)
}

func renderRBAC(deployDir, projectName string) error {
Expand All @@ -170,9 +171,24 @@ func renderRBAC(deployDir, projectName string) error {
return writeFileAndPrint(filepath.Join(deployDir, rbacYaml), buf.Bytes(), defaultFileMode)
}

// RenderDeployFiles generates "deploy/operator.yaml"
// TODO: rethink about when/what to generate when invoking build command.
func RenderDeployFiles(c *Config, image string) error {
func renderDeployFiles(deployDir, projectName, apiVersion, kind string) error {
buf := &bytes.Buffer{}
if err := renderRBACYaml(buf, projectName); err != nil {
return err
}
if err := writeFileAndPrint(filepath.Join(deployDir, rbacYaml), buf.Bytes(), defaultFileMode); err != nil {
return err
}

buf = &bytes.Buffer{}
if err := renderCustomResourceYaml(buf, apiVersion, kind); err != nil {
return err
}
return writeFileAndPrint(filepath.Join(deployDir, crYaml), buf.Bytes(), defaultFileMode)
}

// RenderOperatorYaml generates "deploy/operator.yaml"
func RenderOperatorYaml(c *Config, image string) error {
buf := &bytes.Buffer{}
if err := renderOperatorYaml(buf, c.Kind, c.APIVersion, c.ProjectName, image); err != nil {
return err
Expand Down Expand Up @@ -238,7 +254,8 @@ func renderCodegenFiles(codegenDir, repoPath, apiDirName, version, projectName s

func (g *Generator) renderPkg() error {
v := version(g.apiVersion)
apiDir := filepath.Join(g.projectName, apisDir, apiDirName(g.apiVersion), v)
adn := apiDirName(g.apiVersion)
apiDir := filepath.Join(g.projectName, apisDir, adn, v)
if err := os.MkdirAll(apiDir, defaultDirFileMode); err != nil {
return err
}
Expand All @@ -250,7 +267,7 @@ func (g *Generator) renderPkg() error {
if err := os.MkdirAll(sDir, defaultDirFileMode); err != nil {
return err
}
return renderStubFiles(sDir)
return renderStubFiles(sDir, g.repoPath, g.kind, adn, v)
}

func renderAPIFiles(apiDir, groupName, version, kind string) error {
Expand All @@ -277,9 +294,9 @@ func renderAPIFiles(apiDir, groupName, version, kind string) error {
return writeFileAndPrint(filepath.Join(apiDir, types), buf.Bytes(), defaultFileMode)
}

func renderStubFiles(stubDir string) error {
func renderStubFiles(stubDir, repoPath, kind, apiDirName, version string) error {
buf := &bytes.Buffer{}
if err := renderHandlerFile(buf); err != nil {
if err := renderHandlerFile(buf, repoPath, kind, apiDirName, version); err != nil {
return err
}
return writeFileAndPrint(filepath.Join(stubDir, handler), buf.Bytes(), defaultFileMode)
Expand Down
73 changes: 63 additions & 10 deletions pkg/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ import (
"testing"
)

const (
// test constants for app-operator
appRepoPath = "github.com/example-inc/app-operator"
appKind = "App"
appApiDirName = "app"
appAPIVersion = appGroupName + "/" + appVersion
appVersion = "v1alpha1"
appGroupName = "app.example.com"
)

const mainExp = `package main

import (
"context"
"runtime"

stub "github.com/coreos/play/pkg/stub"
stub "github.com/example-inc/app-operator/pkg/stub"
sdk "github.com/coreos/operator-sdk/pkg/sdk"
sdkVersion "github.com/coreos/operator-sdk/version"
sdkVersion "github.com/coreos/operator-sdk/version"

"github.com/sirupsen/logrus"
)
Expand All @@ -40,15 +50,15 @@ func printVersion() {

func main() {
printVersion()
sdk.Watch("apps/v1", "Deployment", "default")
sdk.Watch("app.example.com/v1alpha1", "App", "default")
sdk.Handle(stub.NewHandler())
sdk.Run(context.TODO())
sdk.Run(context.TODO())
}
`

func TestGenMain(t *testing.T) {
buf := &bytes.Buffer{}
if err := renderMainFile(buf, "github.com/coreos/play"); err != nil {
if err := renderMainFile(buf, appRepoPath, appAPIVersion, appKind); err != nil {
t.Error(err)
return
}
Expand All @@ -61,10 +71,16 @@ func TestGenMain(t *testing.T) {
const handlerExp = `package stub

import (
"github.com/example-inc/app-operator/pkg/apis/app/v1alpha1"

"github.com/coreos/operator-sdk/pkg/sdk/action"
"github.com/coreos/operator-sdk/pkg/sdk/handler"
"github.com/coreos/operator-sdk/pkg/sdk/types"
"github.com/sirupsen/logrus"
apps_v1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func NewHandler() handler.Handler {
Expand All @@ -76,18 +92,55 @@ type Handler struct {
}

func (h *Handler) Handle(ctx types.Context, event types.Event) error {
// Change me
switch o := event.Object.(type) {
case *apps_v1.Deployment:
logrus.Printf("Received Deployment: %v", o.Name)
case *v1alpha1.App:
err := action.Create(newbusyBoxPod(o))
if err != nil && !errors.IsAlreadyExists(err) {
logrus.Errorf("Failed to create busybox pod : %v", err)
return err
}
}
return nil
}

// newbusyBoxPod demonstrates how to create a busybox pod
func newbusyBoxPod(cr *v1alpha1.App) *v1.Pod {
labels := map[string]string{
"app": "busy-box",
}
return &v1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "busy-box",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(cr, schema.GroupVersionKind{
Group: v1alpha1.SchemeGroupVersion.Group,
Version: v1alpha1.SchemeGroupVersion.Version,
Kind: "App",
}),
},
Labels: labels,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "busybox",
Image: "busybox",
Command: []string{"sleep", "3600"},
},
},
},
}
}
`

func TestGenHandler(t *testing.T) {
buf := &bytes.Buffer{}
if err := renderHandlerFile(buf); err != nil {
if err := renderHandlerFile(buf, appRepoPath, appKind, appApiDirName, appVersion); err != nil {
t.Error(err)
return
}
Expand Down
51 changes: 47 additions & 4 deletions pkg/generator/handler_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ package generator
const handlerTmpl = `package stub

import (
"{{.RepoPath}}/pkg/apis/{{.APIDirName}}/{{.Version}}"

"{{.OperatorSDKImport}}/action"
"{{.OperatorSDKImport}}/handler"
"{{.OperatorSDKImport}}/types"
"github.com/sirupsen/logrus"
apps_v1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func NewHandler() handler.Handler {
Expand All @@ -33,11 +39,48 @@ type Handler struct {
}

func (h *Handler) Handle(ctx types.Context, event types.Event) error {
// Change me
switch o := event.Object.(type) {
case *apps_v1.Deployment:
logrus.Printf("Received Deployment: %v", o.Name)
case *{{.Version}}.{{.Kind}}:
err := action.Create(newbusyBoxPod(o))
if err != nil && !errors.IsAlreadyExists(err) {
logrus.Errorf("Failed to create busybox pod : %v", err)
return err
}
}
return nil
}

// newbusyBoxPod demonstrates how to create a busybox pod
func newbusyBoxPod(cr *{{.Version}}.{{.Kind}}) *v1.Pod {
labels := map[string]string{
"app": "busy-box",
}
return &v1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "busy-box",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(cr, schema.GroupVersionKind{
Group: {{.Version}}.SchemeGroupVersion.Group,
Version: {{.Version}}.SchemeGroupVersion.Version,
Kind: "{{.Kind}}",
}),
},
Labels: labels,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "busybox",
Image: "busybox",
Command: []string{"sleep", "3600"},
},
},
},
}
}
`
Loading