Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operator Skeleton Generators #1222

Merged
merged 24 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6521271
starting skeleton operator creation
kensipe Dec 6, 2019
3062108
add params started and working
kensipe Dec 20, 2019
45dbad8
add parameter working with plans
kensipe Dec 20, 2019
30b8061
working add task generator without pipes
kensipe Dec 21, 2019
38b3959
refactoring to encapsulate prompts for kudo objects and adding godoc
kensipe Dec 21, 2019
410eaab
added pipe tasks
kensipe Dec 22, 2019
5f2f663
adding structure for add plan, redefine cli docs and godocs
kensipe Dec 22, 2019
5b95503
adding structure for add plan, redefine cli docs and godocs
kensipe Dec 22, 2019
eb19763
list of things todo
kensipe Dec 23, 2019
87a2d1e
working version of add plan. needs better ux
kensipe Dec 23, 2019
8347140
working version for add plan
kensipe Dec 23, 2019
7618b33
organized and forced pipe kinds to secret or configmap
kensipe Dec 23, 2019
c484174
give option to not require tasks, reduce task list to those task not …
kensipe Dec 23, 2019
abb9745
clog levels are wrong. needed for tracing but otherwise should not b…
kensipe Dec 23, 2019
4b5be7e
adding confirmation to add a trigger for params. validation added to…
kensipe Dec 23, 2019
a704f63
test for maintainer
kensipe Dec 31, 2019
5c4e4e4
test for adding parameters
kensipe Dec 31, 2019
81070a1
test for adding plans
kensipe Dec 31, 2019
661ca79
test for adding tasks
kensipe Dec 31, 2019
34e900f
clean up
kensipe Dec 31, 2019
ca5c539
updating based on pr feedback
kensipe Jan 6, 2020
9ea5451
Merge branch 'master' into ken/skeleton-generators
kensipe Jan 7, 2020
26f7fbe
Update pkg/kudoctl/cmd/generate/task.go
kensipe Jan 7, 2020
82ad31d
happy linter
kensipe Jan 7, 2020
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
12 changes: 6 additions & 6 deletions pkg/apis/kudo/v1beta1/operatorversion_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,26 +141,26 @@ type TaskSpec struct {
type ResourceTaskSpec struct {
// +optional
// +nullable
Resources []string `json:"resources"`
Resources []string `json:"resources,omitempty"`
}

// DummyTaskSpec can succeed or fail on demand and is very useful for testing operators
type DummyTaskSpec struct {
// +optional
WantErr bool `json:"wantErr"`
WantErr bool `json:"wantErr,omitempty"`
// +optional
Fatal bool `json:"fatal"`
Fatal bool `json:"fatal,omitempty"`
// +optional
Done bool `json:"done"`
Done bool `json:"done,omitempty"`
}

// PipeTask specifies a task that generates files and stores them for later usage in subsequent tasks
type PipeTaskSpec struct {
// +optional
Pod string `json:"pod"`
Pod string `json:"pod,omitempty"`
// +optional
// +nullable
Pipe []PipeSpec `json:"pipe"`
Pipe []PipeSpec `json:"pipe,omitempty"`
}

// PipeSpec describes how a file generated by a PipeTask is stored and referenced
Expand Down
32 changes: 32 additions & 0 deletions pkg/kudoctl/cmd/generate/maintainer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package generate

import (
"github.com/spf13/afero"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages/reader"
)

// AddMaintainer adds a maintainer to the operator.yaml
func AddMaintainer(fs afero.Fs, path string, m *v1beta1.Maintainer) error {

p, err := reader.ReadDir(fs, path)
if err != nil {
return err
}
o := p.Files.Operator

o.Maintainers = append(o.Maintainers, m)

return writeOperator(fs, path, o)
}

// MaintainerList provides a list of operator maintainers
func MaintainerList(fs afero.Fs, path string) ([]*v1beta1.Maintainer, error) {
p, err := reader.ReadDir(fs, path)
if err != nil {
return nil, err
}

return p.Files.Operator.Maintainers, nil
}
56 changes: 56 additions & 0 deletions pkg/kudoctl/cmd/generate/maintainer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package generate

import (
"flag"
"io/ioutil"
"path/filepath"
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/files"
)

var updateGolden = flag.Bool("update", false, "update .golden files and manifests in /config/crd")

func TestAddMaintainer(t *testing.T) {
goldenFile := "maintainer"
fs := afero.NewMemMapFs()
files.CopyOperatorToFs(fs, "../../packages/testdata/zk", "/opt")
m := v1beta1.Maintainer{
Name: "Cat in the hat",
Email: "c@hat.com",
}

err := AddMaintainer(fs, "/opt/zk", &m)
assert.NoError(t, err)

operator, err := afero.ReadFile(fs, "/opt/zk/operator.yaml")
assert.NoError(t, err)

gp := filepath.Join("testdata", goldenFile+".golden")

if *updateGolden {
t.Logf("updating golden file %s", goldenFile)
if err := ioutil.WriteFile(gp, operator, 0644); err != nil {
t.Fatalf("failed to update golden file: %s", err)
}
}
g, err := ioutil.ReadFile(gp)
if err != nil {
t.Fatalf("failed reading .golden: %s", err)
}

assert.Equal(t, g, operator, "for golden file: %s", gp)
}

func TestListMaintainers(t *testing.T) {
fs := afero.OsFs{}
m, err := MaintainerList(fs, "../../packages/testdata/zk")
assert.NoError(t, err)

assert.Equal(t, 3, len(m))
assert.Equal(t, "Alena Varkockova", m[0].Name)
}
35 changes: 33 additions & 2 deletions pkg/kudoctl/cmd/generate/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"path"
"path/filepath"

"github.com/spf13/afero"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -36,7 +37,7 @@ func CanGenerateOperator(fs afero.Fs, dir string, overwrite bool) error {
}

// Operator generates an initial operator folder with a operator.yaml
func Operator(fs afero.Fs, dir string, op packages.OperatorFile, overwrite bool) error {
func Operator(fs afero.Fs, dir string, op *packages.OperatorFile, overwrite bool) error {
err := CanGenerateOperator(fs, dir, overwrite)
if err != nil {
return err
Expand Down Expand Up @@ -90,7 +91,7 @@ func writeParameters(fs afero.Fs, dir string, params packages.ParamsFile) error
return afero.WriteFile(fs, fname, p, 0755)
}

func writeOperator(fs afero.Fs, dir string, op packages.OperatorFile) error {
func writeOperator(fs afero.Fs, dir string, op *packages.OperatorFile) error {
o, err := yaml.Marshal(op)
if err != nil {
return err
Expand All @@ -99,3 +100,33 @@ func writeOperator(fs afero.Fs, dir string, op packages.OperatorFile) error {
fname := path.Join(dir, reader.OperatorFileName)
return afero.WriteFile(fs, fname, o, 0755)
}

// OperatorPath determines the path to use as operator path for generators
// the path is either current "", or a dir with operator.yaml (if 1) else an error
// and is determined based on location of operator.yaml
func OperatorPath(fs afero.Fs) (string, error) {
fname := "operator.yaml"

exists, err := afero.Exists(fs, fname)
if err != nil {
return "", err
}

if exists {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stylistic, mind adding whitespace between each } and next statement?

return "", nil
}

pat := path.Join("**", fname)
// one more try
files, err := afero.Glob(fs, pat)
if err != nil {
return "", err
}
if len(files) < 1 {
return "", errors.New("no operator folder discovered")
}
if len(files) > 1 {
return "", errors.New("multiple operator folders discovered")
}
return filepath.Dir(files[0]), nil
}
8 changes: 4 additions & 4 deletions pkg/kudoctl/cmd/generate/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestOperator_Write(t *testing.T) {

fs := afero.NewMemMapFs()

err := Operator(fs, "operator", op1, false)
err := Operator(fs, "operator", &op1, false)
// no error on create
assert.Nil(t, err)

Expand All @@ -59,11 +59,11 @@ func TestOperator_Write(t *testing.T) {
assert.True(t, exists)

// test fail on existing
err = Operator(fs, "operator", op1, false)
err = Operator(fs, "operator", &op1, false)
assert.Errorf(t, err, "folder 'operator' already exists")

// test overwriting with no error
err = Operator(fs, "operator", op1, true)
err = Operator(fs, "operator", &op1, true)
// no error on overwrite
assert.Nil(t, err)

Expand All @@ -76,7 +76,7 @@ func TestOperator_Write(t *testing.T) {
err = writeParameters(fs, "operator", pf)
assert.Nil(t, err)
// test overwriting with no error
err = Operator(fs, "operator", op1, true)
err = Operator(fs, "operator", &op1, true)
// no error on overwrite
assert.Nil(t, err)
parmfile, _ := afero.ReadFile(fs, paramFilename)
Expand Down
35 changes: 35 additions & 0 deletions pkg/kudoctl/cmd/generate/parameter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package generate

import (
"github.com/spf13/afero"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages/reader"
)

// AddParameter writes a parameter to the params.yaml file
func AddParameter(fs afero.Fs, path string, p *v1beta1.Parameter) error {

pf, err := reader.ReadDir(fs, path)
if err != nil {
return err
}

params := pf.Files.Params
params.Parameters = append(params.Parameters, *p)

return writeParameters(fs, path, *params)
}

func ParameterNameList(fs afero.Fs, path string) (paramNames []string, err error) {
pf, err := reader.ReadDir(fs, path)
if err != nil {
return nil, err
}

for _, parameter := range pf.Files.Params.Parameters {
paramNames = append(paramNames, parameter.Name)
}

return paramNames, nil
}
71 changes: 71 additions & 0 deletions pkg/kudoctl/cmd/generate/parameter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package generate

import (
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/files"
)

func TestAddParameter(t *testing.T) {
goldenFile := "parameter"
path := "/opt/zk"
fs := afero.NewMemMapFs()
files.CopyOperatorToFs(fs, "../../packages/testdata/zk", "/opt")

bar := "Bar"
p := v1beta1.Parameter{
Name: "Foo",
Default: &bar,
}

err := AddParameter(fs, path, &p)
assert.NoError(t, err)

params, err := afero.ReadFile(fs, "/opt/zk/params.yaml")
assert.NoError(t, err)

gp := filepath.Join("testdata", goldenFile+".golden")

if *updateGolden {
t.Logf("updating golden file %s", goldenFile)
if err := ioutil.WriteFile(gp, params, 0644); err != nil {
t.Fatalf("failed to update golden file: %s", err)
}
}
golden, err := ioutil.ReadFile(gp)
if err != nil {
t.Fatalf("failed reading .golden: %s", err)
}

assert.Equal(t, golden, params, "for golden file: %s", gp)
}

func TestAddParameter_bad_path(t *testing.T) {
path, _ := os.Getwd()
fs := afero.OsFs{}

bar := "Bar"
p := v1beta1.Parameter{
Name: "Foo",
Default: &bar,
}

err := AddParameter(fs, path, &p)
assert.Error(t, err)
}

func TestListParams(t *testing.T) {
fs := afero.OsFs{}
ps, err := ParameterNameList(fs, "../../packages/testdata/zk")
assert.NoError(t, err)

assert.Equal(t, 2, len(ps))
assert.Equal(t, "memory", ps[0])
}
51 changes: 51 additions & 0 deletions pkg/kudoctl/cmd/generate/plans.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package generate

import (
"sort"

"github.com/spf13/afero"

"github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1"
"github.com/kudobuilder/kudo/pkg/kudoctl/packages/reader"
)

// AddPlan adds a plan to the operator.yaml file
func AddPlan(fs afero.Fs, path string, planName string, plan *v1beta1.Plan) error {

pf, err := reader.ReadDir(fs, path)
if err != nil {
return err
}

o := pf.Files.Operator
plans := o.Plans
plans[planName] = *plan
pf.Files.Operator.Plans = plans

return writeOperator(fs, path, o)
}

// PlanList provides a list of operator plans
func PlanList(fs afero.Fs, path string) (map[string]v1beta1.Plan, error) {
p, err := reader.ReadDir(fs, path)
if err != nil {
return nil, err
}

return p.Files.Operator.Plans, nil
}

// PlanNameList provides a list of operator plan names
func PlanNameList(fs afero.Fs, path string) ([]string, error) {

names := []string{}
p, err := reader.ReadDir(fs, path)
if err != nil {
return nil, err
}
for name := range p.Files.Operator.Plans {
names = append(names, name)
}
sort.Strings(names)
return names, nil
}
Loading