diff --git a/Gopkg.lock b/Gopkg.lock index f8180644e3..787bc5ab10 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -404,6 +404,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d5cc928bcf8f796babddedb13b1516b8d6fbc2f48d68546b712af753671673e2" + inputs-digest = "f310ffe8b6a0cff2978d4f2e11c7f32dcb008225c1c9193e9f7da20f34935189" solver-name = "gps-cdcl" solver-version = 1 diff --git a/pkg/generator/gen_api_doc.go b/pkg/generator/gen_api_doc.go deleted file mode 100644 index bfb9f3f476..0000000000 --- a/pkg/generator/gen_api_doc.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Doc contains all the customized data needed to generate apis///doc.go for a new operator -// when pairing with apisDocTmpl template. -type Doc struct { - GroupName string - Version string -} - -// renderAPIDocFile generates the apis///doc.go file. -func renderAPIDocFile(w io.Writer, groupName, version string) error { - t := template.New("apis///doc.go") - t, err := t.Parse(apiDocTmpl) - if err != nil { - return err - } - - d := Doc{ - GroupName: groupName, - Version: version, - } - return t.Execute(w, d) -} diff --git a/pkg/generator/gen_api_register.go b/pkg/generator/gen_api_register.go deleted file mode 100644 index b5e2d756f2..0000000000 --- a/pkg/generator/gen_api_register.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "strings" - "text/template" -) - -const pluralSuffix = "s" - -// Register contains all the customized data needed to generate apis///register.go -// for a new operator when pairing with apisDocTmpl template. -type Register struct { - GroupName string - Version string - Kind string - KindPlural string -} - -// renderAPIRegisterFile generates the apis///register.go file. -func renderAPIRegisterFile(w io.Writer, kind, groupName, version string) error { - t := template.New("apis///register.go") - t, err := t.Parse(apiRegisterTmpl) - if err != nil { - return err - } - - d := Register{ - GroupName: groupName, - Version: version, - Kind: kind, - // TODO: adding "s" to make a word plural is too native - // and is wrong for many special nouns. Make this better. - KindPlural: strings.ToLower(kind) + pluralSuffix, - } - return t.Execute(w, d) -} diff --git a/pkg/generator/gen_api_register_test.go b/pkg/generator/gen_api_register_test.go deleted file mode 100644 index 443c2831d6..0000000000 --- a/pkg/generator/gen_api_register_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const registerExp = `package v1alpha1 - -import ( - sdkK8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -const ( - version = "v1alpha1" - groupName = "app.example.com" -) - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme - // SchemeGroupVersion is the group version used to register these objects. - SchemeGroupVersion = schema.GroupVersion{Group: groupName, Version: version} -) - -func init() { - sdkK8sutil.AddToSDKScheme(AddToScheme) -} - -// addKnownTypes adds the set of types defined in this package to the supplied scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &AppService{}, - &AppServiceList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} -` - -func TestGenRegister(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderAPIRegisterFile(buf, appKind, appGroupName, appVersion); err != nil { - t.Error(err) - return - } - if registerExp != buf.String() { - t.Errorf(errorMessage, registerExp, buf.String()) - } -} diff --git a/pkg/generator/gen_api_types.go b/pkg/generator/gen_api_types.go deleted file mode 100644 index 46b39aa666..0000000000 --- a/pkg/generator/gen_api_types.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Types contains all the customized data needed to generate apis///types.go -// for a new operator when pairing with apisTypesTmpl template. -type Types struct { - Version string - Kind string -} - -// renderAPITypesFile generates the apis///types.go file. -func renderAPITypesFile(w io.Writer, kind, version string) error { - t := template.New("apis///types.go") - t, err := t.Parse(apiTypesTmpl) - if err != nil { - return err - } - - types := Types{ - Version: version, - Kind: kind, - } - return t.Execute(w, types) -} diff --git a/pkg/generator/gen_api_types_test.go b/pkg/generator/gen_api_types_test.go deleted file mode 100644 index b49062446a..0000000000 --- a/pkg/generator/gen_api_types_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const typesExp = `package app.example.com/v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type AppServiceList struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`\n" + - ` metav1.ListMeta ` + "`" + `json:"metadata"` + "`\n" + - ` Items []AppService ` + "`" + `json:"items"` + "`" + ` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type AppService struct { - metav1.TypeMeta ` + "`" + `json:",inline"` + "`\n" + - ` metav1.ObjectMeta ` + "`" + `json:"metadata"` + "`\n" + - ` Spec AppServiceSpec ` + "`" + `json:"spec"` + "`\n" + - ` Status AppServiceStatus ` + "`" + `json:"status,omitempty"` + "`" + ` -} - -type AppServiceSpec struct { - // Fill me -} -type AppServiceStatus struct { - // Fill me -} -` - -func TestGenTypes(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderAPITypesFile(buf, appKind, appAPIVersion); err != nil { - t.Error(err) - return - } - if typesExp != buf.String() { - t.Errorf(errorMessage, typesExp, buf.String()) - } -} diff --git a/pkg/generator/gen_build.go b/pkg/generator/gen_build.go deleted file mode 100644 index 88122461d3..0000000000 --- a/pkg/generator/gen_build.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Build contains all the customized data needed to generate tmp/build.sh -// for a new operator when pairing with buildTmpl template. -type Build struct { - RepoPath string - ProjectName string -} - -// renderBuildFile generates the tmp/build/build.sh file given a repo path ("github.com/coreos/app-operator") -// and projectName ("app-operator"). -func renderBuildFile(w io.Writer, repo, projectName string) error { - t := template.New("tmp/build/build.sh") - t, err := t.Parse(buildTmpl) - if err != nil { - return err - } - - m := Build{ - RepoPath: repo, - ProjectName: projectName, - } - return t.Execute(w, m) -} - -// renderDockerBuildFile generates the docker_build.sh script which builds the docker image for this operator. -func renderDockerBuildFile(w io.Writer) error { - _, err := w.Write([]byte(dockerBuildTmpl)) - return err -} - -// DockerFile contains all the customized data needed to generate tmp/build/Dockerfie -// for a new operator when pairing with dockerFileTmpl template. -type DockerFile struct { - ProjectName string -} - -// renderDockerFile generates the tmp/build/Dockerfile file given the projectName ("app-operator"). -func renderDockerFile(w io.Writer, projectName string) error { - t := template.New("tmp/build/Dockerfile") - t, err := t.Parse(dockerFileTmpl) - if err != nil { - return err - } - - df := DockerFile{ - ProjectName: projectName, - } - return t.Execute(w, df) -} diff --git a/pkg/generator/gen_build_test.go b/pkg/generator/gen_build_test.go deleted file mode 100644 index 7b28f49bca..0000000000 --- a/pkg/generator/gen_build_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const buildExp = `#!/usr/bin/env bash - -set -o errexit -set -o nounset -set -o pipefail - -if ! which go > /dev/null; then - echo "golang needs to be installed" - exit 1 -fi - -BIN_DIR="$(pwd)/tmp/_output/bin" -mkdir -p ${BIN_DIR} -PROJECT_NAME="app-operator" -REPO_PATH="github.com/example-inc/app-operator" -BUILD_PATH="${REPO_PATH}/cmd/${PROJECT_NAME}" -echo "building "${PROJECT_NAME}"..." -GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${BIN_DIR}/${PROJECT_NAME} $BUILD_PATH -` - -const dockerFileExp = `FROM alpine:3.6 - -RUN adduser -D app-operator -USER app-operator - -ADD tmp/_output/bin/app-operator /usr/local/bin/app-operator -` - -func TestGenBuild(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderBuildFile(buf, appRepoPath, appProjectName); err != nil { - t.Error(err) - return - } - if buildExp != buf.String() { - t.Errorf(errorMessage, buildExp, buf.String()) - } - - buf = &bytes.Buffer{} - if err := renderDockerBuildFile(buf); err != nil { - t.Error(err) - return - } - if dockerBuildTmpl != buf.String() { - t.Errorf(errorMessage, dockerBuildTmpl, buf.String()) - } - - buf = &bytes.Buffer{} - if err := renderDockerFile(buf, appProjectName); err != nil { - t.Error(err) - return - } - if dockerFileExp != buf.String() { - t.Errorf(errorMessage, dockerFileExp, buf.String()) - } -} diff --git a/pkg/generator/gen_codegen.go b/pkg/generator/gen_codegen.go deleted file mode 100644 index f163c5cee4..0000000000 --- a/pkg/generator/gen_codegen.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Boilerplate contains all the customized data needed to generate codegen/boilerplate.go.txt -// for a new operator when pairing with boilerplateTmpl template. -type Boilerplate struct { - ProjectName string -} - -// UpdateGenerated contains all the customized data needed to generate codegen/update-generated.sh -// for a new operator when pairing with updateGeneratedTmpl template. -type UpdateGenerated struct { - RepoPath string - APIDirName string - Version string -} - -func renderBoilerplateFile(w io.Writer, projectName string) error { - t := template.New("codegen/boilerplate.go.txt") - t, err := t.Parse(boilerplateTmpl) - if err != nil { - return err - } - - b := Boilerplate{ - ProjectName: projectName, - } - return t.Execute(w, b) -} - -func renderUpdateGeneratedFile(w io.Writer, repo, apiDirName, version string) error { - t := template.New("codegen/update-generated.sh") - t, err := t.Parse(updateGeneratedTmpl) - if err != nil { - return err - } - - b := UpdateGenerated{ - RepoPath: repo, - APIDirName: apiDirName, - Version: version, - } - return t.Execute(w, b) -} diff --git a/pkg/generator/gen_codegen_test.go b/pkg/generator/gen_codegen_test.go deleted file mode 100644 index f43b89df3e..0000000000 --- a/pkg/generator/gen_codegen_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const boilerplateExp = ` -` - -const updateGeneratedExp = `#!/usr/bin/env bash - -set -o errexit -set -o nounset -set -o pipefail - -DOCKER_REPO_ROOT="/go/src/github.com/example-inc/app-operator" -IMAGE=${IMAGE:-"gcr.io/coreos-k8s-scale-testing/codegen:1.9.3"} - -docker run --rm \ - -v "$PWD":"$DOCKER_REPO_ROOT":Z \ - -w "$DOCKER_REPO_ROOT" \ - "$IMAGE" \ - "/go/src/k8s.io/code-generator/generate-groups.sh" \ - "deepcopy" \ - "github.com/example-inc/app-operator/pkg/generated" \ - "github.com/example-inc/app-operator/pkg/apis" \ - "app:v1alpha1" \ - --go-header-file "./tmp/codegen/boilerplate.go.txt" \ - $@ -` - -func TestCodeGen(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderBoilerplateFile(buf, appProjectName); err != nil { - t.Error(err) - return - } - if boilerplateExp != buf.String() { - t.Errorf(errorMessage, boilerplateExp, buf.String()) - } - - buf = &bytes.Buffer{} - if err := renderUpdateGeneratedFile(buf, appRepoPath, appApiDirName, appVersion); err != nil { - t.Error(err) - return - } - if updateGeneratedExp != buf.String() { - t.Errorf(errorMessage, updateGeneratedExp, buf.String()) - } -} diff --git a/pkg/generator/gen_deploy.go b/pkg/generator/gen_deploy.go deleted file mode 100644 index 9bf7e1c734..0000000000 --- a/pkg/generator/gen_deploy.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "fmt" - "io" - "strings" - "text/template" -) - -const ( - crdTmplName = "deploy/crd.yaml" - operatorTmplName = "deploy/operator.yaml" - rbacTmplName = "deploy/rbac.yaml" - crTmplName = "deploy/cr.yaml" -) - -// CRDYaml contains data needed to generate deploy/crd.yaml -type CRDYaml struct { - Kind string - KindSingular string - KindPlural string - GroupName string - Version string -} - -// renderCRDYaml generates deploy/crd.yaml -func renderCRDYaml(w io.Writer, kind, apiVersion string) error { - t := template.New(crdTmplName) - t, err := t.Parse(crdYamlTmpl) - if err != nil { - return fmt.Errorf("failed to parse crd yaml template: %v", err) - } - - ks := strings.ToLower(kind) - o := CRDYaml{ - Kind: kind, - KindSingular: ks, - KindPlural: toPlural(ks), - GroupName: groupName(apiVersion), - Version: version(apiVersion), - } - return t.Execute(w, o) -} - -// OperatorYaml contains data needed to generate deploy/operator.yaml -type OperatorYaml struct { - ProjectName string - Image string -} - -// renderOperatorYaml generates deploy/operator.yaml. -func renderOperatorYaml(w io.Writer, 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) - } - - o := OperatorYaml{ - ProjectName: projectName, - Image: image, - } - return t.Execute(w, o) -} - -// RBACYaml contains all the customized data needed to generate deploy/rbac.yaml for a new operator -// when pairing with rbacYamlTmpl template. -type RBACYaml struct { - ProjectName string - GroupName string -} - -// renderRBACYaml generates deploy/rbac.yaml. -func renderRBACYaml(w io.Writer, projectName, groupName string) error { - t := template.New(rbacTmplName) - t, err := t.Parse(rbacYamlTmpl) - if err != nil { - return fmt.Errorf("failed to parse rbac yaml template: %v", err) - } - - r := RBACYaml{ - ProjectName: projectName, - GroupName: groupName, - } - 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) -} diff --git a/pkg/generator/gen_deploy_test.go b/pkg/generator/gen_deploy_test.go deleted file mode 100644 index d9586f54d8..0000000000 --- a/pkg/generator/gen_deploy_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package generator - -import ( - "bytes" - "testing" -) - -const crdYamlExp = `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 -` - -const operatorYamlExp = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: app-operator -spec: - replicas: 1 - selector: - matchLabels: - name: app-operator - template: - metadata: - labels: - name: app-operator - spec: - containers: - - name: app-operator - image: quay.io/example-inc/app-operator:0.0.1 - command: - - app-operator - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace -` - -const rbacYamlExp = `kind: Role -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: app-operator -rules: -- apiGroups: - - app.example.com - resources: - - "*" - verbs: - - "*" -- apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - "*" -- apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - "*" - ---- - -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: default-account-app-operator -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: app-operator - apiGroup: rbac.authorization.k8s.io -` - -func TestGenDeploy(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderCRDYaml(buf, appKind, appAPIVersion); err != nil { - t.Error(err) - } - if crdYamlExp != buf.String() { - t.Errorf(errorMessage, crdYamlExp, buf.String()) - } - - buf = &bytes.Buffer{} - if err := renderOperatorYaml(buf, appProjectName, appImage); err != nil { - t.Error(err) - } - if operatorYamlExp != buf.String() { - t.Errorf(errorMessage, operatorYamlExp, buf.String()) - } - - buf = &bytes.Buffer{} - if err := renderRBACYaml(buf, appProjectName, appGroupName); err != nil { - t.Error(err) - } - if rbacYamlExp != buf.String() { - t.Errorf(errorMessage, rbacYamlExp, buf.String()) - } -} diff --git a/pkg/generator/gen_handler.go b/pkg/generator/gen_handler.go deleted file mode 100644 index cac2739fdc..0000000000 --- a/pkg/generator/gen_handler.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Handler contains all the customized data needed to generate stub/handler.go for a new operator -// when pairing with handlerTmpl template. -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, repoPath, kind, apiDirName, version string) error { - t := template.New("stub/handler.go") - t, err := t.Parse(handlerTmpl) - if err != nil { - return err - } - - h := Handler{ - OperatorSDKImport: sdkImport, - RepoPath: repoPath, - Kind: kind, - APIDirName: apiDirName, - Version: version, - } - return t.Execute(w, h) -} diff --git a/pkg/generator/gen_handler_test.go b/pkg/generator/gen_handler_test.go deleted file mode 100644 index 1b31ea3b5d..0000000000 --- a/pkg/generator/gen_handler_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const handlerExp = `package stub - -import ( - "context" - - "github.com/example-inc/app-operator/pkg/apis/app/v1alpha1" - - "github.com/operator-framework/operator-sdk/pkg/sdk" - "github.com/sirupsen/logrus" - corev1 "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() sdk.Handler { - return &Handler{} -} - -type Handler struct { - // Fill me -} - -func (h *Handler) Handle(ctx context.Context, event sdk.Event) error { - switch o := event.Object.(type) { - case *v1alpha1.AppService: - err := sdk.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.AppService) *corev1.Pod { - labels := map[string]string{ - "app": "busy-box", - } - return &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "busy-box", - Namespace: cr.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cr, schema.GroupVersionKind{ - Group: v1alpha1.SchemeGroupVersion.Group, - Version: v1alpha1.SchemeGroupVersion.Version, - Kind: "AppService", - }), - }, - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "busybox", - Image: "busybox", - Command: []string{"sleep", "3600"}, - }, - }, - }, - } -} -` - -func TestGenHandler(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderHandlerFile(buf, appRepoPath, appKind, appApiDirName, appVersion); err != nil { - t.Error(err) - return - } - if handlerExp != buf.String() { - t.Errorf(errorMessage, handlerExp, buf.String()) - } -} diff --git a/pkg/generator/gen_main.go b/pkg/generator/gen_main.go deleted file mode 100644 index 5f56a1d653..0000000000 --- a/pkg/generator/gen_main.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "path/filepath" - "text/template" -) - -const ( - // sdkImport is the operator-sdk import path. - sdkImport = "github.com/operator-framework/operator-sdk/pkg/sdk" - k8sutilImport = "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" - versionImport = "github.com/operator-framework/operator-sdk/version" -) - -// Main contains all the customized data needed to generate cmd//main.go for a new operator -// when pairing with mainTmpl template. -type Main struct { - // imports - OperatorSDKImport string - StubImport string - K8sutilImport string - SDKVersionImport string - - APIVersion string - Kind string -} - -// renderMainFile generates the cmd//main.go file. -func renderMainFile(w io.Writer, repo, apiVersion, kind string) error { - t := template.New("cmd//main.go") - t, err := t.Parse(mainTmpl) - if err != nil { - return err - } - - m := Main{ - OperatorSDKImport: sdkImport, - StubImport: filepath.Join(repo, stubDir), - K8sutilImport: k8sutilImport, - SDKVersionImport: versionImport, - APIVersion: apiVersion, - Kind: kind, - } - return t.Execute(w, m) -} diff --git a/pkg/generator/gen_main_test.go b/pkg/generator/gen_main_test.go deleted file mode 100644 index 41b1ca995f..0000000000 --- a/pkg/generator/gen_main_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "bytes" - "testing" -) - -const mainExp = `package main - -import ( - "context" - "runtime" - - stub "github.com/example-inc/app-operator/pkg/stub" - sdk "github.com/operator-framework/operator-sdk/pkg/sdk" - k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" - sdkVersion "github.com/operator-framework/operator-sdk/version" - - "github.com/sirupsen/logrus" -) - -func printVersion() { - logrus.Infof("Go Version: %s", runtime.Version()) - logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) - logrus.Infof("operator-sdk Version: %v", sdkVersion.Version) -} - -func main() { - printVersion() - - resource := "app.example.com/v1alpha1" - kind := "AppService" - namespace, err := k8sutil.GetWatchNamespace() - if err != nil { - logrus.Fatalf("Failed to get watch namespace: %v", err) - } - resyncPeriod := 5 - logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod) - sdk.Watch(resource, kind, namespace, resyncPeriod) - sdk.Handle(stub.NewHandler()) - sdk.Run(context.TODO()) -} -` - -func TestGenMain(t *testing.T) { - buf := &bytes.Buffer{} - if err := renderMainFile(buf, appRepoPath, appAPIVersion, appKind); err != nil { - t.Error(err) - return - } - - if mainExp != buf.String() { - t.Errorf(errorMessage, mainExp, buf.String()) - } -} diff --git a/pkg/generator/gen_olm_catalog.go b/pkg/generator/gen_olm_catalog.go deleted file mode 100644 index a21a08e28a..0000000000 --- a/pkg/generator/gen_olm_catalog.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "fmt" - "io" - "strings" - "text/template" -) - -const ( - // Sample catalog resource values - // TODO: Make this configurable - packageChannel = "alpha" - catalogCRDTmplName = "deploy/olm-catalog/crd.yaml" -) - -// CatalogPackageConfig contains the data needed to generate deploy/olm-catalog/package.yaml -type CatalogPackageConfig struct { - PackageName string - ChannelName string - CurrentCSV string -} - -// renderCatalogPackage generates deploy/olm-catalog/package.yaml -func renderCatalogPackage(w io.Writer, config *Config, catalogVersion string) error { - t := template.New(catalogPackageYaml) - t, err := t.Parse(catalogPackageTmpl) - if err != nil { - return fmt.Errorf("failed to parse catalog package template: %v", err) - } - - name := strings.ToLower(config.Kind) - cpConfig := CatalogPackageConfig{ - PackageName: name, - ChannelName: packageChannel, - CurrentCSV: getCSVName(name, catalogVersion), - } - return t.Execute(w, cpConfig) -} - -// CRDConfig contains the data needed to generate deploy/olm-catalog/crd.yaml -type CRDConfig struct { - Kind string - KindSingular string - KindPlural string - GroupName string - Version string -} - -// renderCatalogCRD generates deploy/olm-catalog/crd.yaml -func renderCatalogCRD(w io.Writer, config *Config) error { - t := template.New(catalogCRDTmplName) - t, err := t.Parse(crdTmpl) - if err != nil { - return fmt.Errorf("failed to parse catalog CRD template: %v", err) - } - - kindSingular := strings.ToLower(config.Kind) - crdConfig := CRDConfig{ - Kind: config.Kind, - KindSingular: kindSingular, - KindPlural: toPlural(kindSingular), - GroupName: groupName(config.APIVersion), - Version: version(config.APIVersion), - } - return t.Execute(w, crdConfig) -} - -// CSVConfig contains the data needed to generate deploy/olm-catalog/csv.yaml -type CSVConfig struct { - Kind string - KindSingular string - KindPlural string - GroupName string - CRDVersion string - ProjectName string - CSVName string - Image string - CatalogVersion string -} - -// renderCatalogCSV generates deploy/olm-catalog/csv.yaml -func renderCatalogCSV(w io.Writer, config *Config, image, catalogVersion string) error { - t := template.New(catalogCSVYaml) - t, err := t.Parse(catalogCSVTmpl) - if err != nil { - return fmt.Errorf("failed to parse catalog CSV template: %v", err) - } - - kindSingular := strings.ToLower(config.Kind) - csvConfig := CSVConfig{ - Kind: config.Kind, - KindSingular: kindSingular, - KindPlural: kindSingular + "s", - GroupName: groupName(config.APIVersion), - CRDVersion: version(config.APIVersion), - CSVName: getCSVName(kindSingular, catalogVersion), - Image: image, - CatalogVersion: catalogVersion, - ProjectName: config.ProjectName, - } - return t.Execute(w, csvConfig) -} - -func getCSVName(name, version string) string { - return name + ".v" + version -} diff --git a/pkg/generator/gen_version.go b/pkg/generator/gen_version.go deleted file mode 100644 index 4d3cede937..0000000000 --- a/pkg/generator/gen_version.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 The Operator-SDK Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package generator - -import ( - "io" - "text/template" -) - -// Version contains the verison number string and gitsha string -type Version struct { - // imports - VersionNumber string -} - -// renderVersionFile generates the version/version.go file. -func renderVersionFile(w io.Writer, versionNumber string) error { - t := template.New("version/version.go") - t, err := t.Parse(versionTmpl) - if err != nil { - return err - } - - v := Version{ - VersionNumber: versionNumber, - } - return t.Execute(w, v) -} diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index df41ae34c2..1e529f8e68 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -17,10 +17,14 @@ package generator import ( "bytes" "fmt" + "io" "io/ioutil" "os" "path/filepath" "strings" + "text/template" + + k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" ) const ( @@ -62,6 +66,18 @@ const ( crdYaml = "crd.yaml" gitignore = ".gitignore" versionfile = "version.go" + + // sdkImport is the operator-sdk import path. + sdkImport = "github.com/operator-framework/operator-sdk/pkg/sdk" + k8sutilImport = "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" + versionImport = "github.com/operator-framework/operator-sdk/version" + packageChannel = "alpha" + catalogCRDTmplName = "deploy/olm-catalog/crd.yaml" + crdTmplName = "deploy/crd.yaml" + operatorTmplName = "deploy/operator.yaml" + rbacTmplName = "deploy/rbac.yaml" + crTmplName = "deploy/cr.yaml" + pluralSuffix = "s" ) type Generator struct { @@ -97,6 +113,10 @@ func NewGenerator(apiVersion, kind, projectName, repoPath string) *Generator { // │ | └── codegen // │ └── version func (g *Generator) Render() error { + if err := g.generateDirStructure(); err != nil { + return err + } + if err := g.renderProject(); err != nil { return err } @@ -123,9 +143,6 @@ func (g *Generator) Render() error { } func (g *Generator) renderProject() error { - if err := os.MkdirAll(g.projectName, defaultDirFileMode); err != nil { - return err - } return renderProjectGitignore(g.projectName) } @@ -157,25 +174,25 @@ func (g *Generator) renderGoDep() error { func (g *Generator) renderCmd() error { cpDir := filepath.Join(g.projectName, cmdDir, g.projectName) - if err := os.MkdirAll(cpDir, defaultDirFileMode); err != nil { - return err - } return renderCmdFiles(cpDir, g.repoPath, g.apiVersion, g.kind) } func renderCmdFiles(cmdProjectDir, repoPath, apiVersion, kind string) error { - buf := &bytes.Buffer{} - if err := renderMainFile(buf, repoPath, apiVersion, kind); err != nil { - return err + td := tmplData{ + OperatorSDKImport: sdkImport, + StubImport: filepath.Join(repoPath, stubDir), + K8sutilImport: k8sutilImport, + SDKVersionImport: versionImport, + APIVersion: apiVersion, + Kind: kind, + MetricsPort: k8sutil.PrometheusMetricsPort, } - return writeFileAndPrint(filepath.Join(cmdProjectDir, main), buf.Bytes(), defaultFileMode) + + return renderWriteFile(filepath.Join(cmdProjectDir, main), "cmd//main.go", mainTmpl, td) } func (g *Generator) renderConfig() error { cp := filepath.Join(g.projectName, configDir) - if err := os.MkdirAll(cp, defaultDirFileMode); err != nil { - return err - } return renderConfigFiles(cp, g.apiVersion, g.kind, g.projectName) } @@ -189,51 +206,54 @@ func renderConfigFiles(configDir, apiVersion, kind, projectName string) error { func (g *Generator) renderDeploy() error { dp := filepath.Join(g.projectName, deployDir) - if err := os.MkdirAll(dp, defaultDirFileMode); err != nil { - return err - } return renderDeployFiles(dp, g.projectName, g.apiVersion, g.kind) } func renderRBAC(deployDir, projectName, groupName string) error { - buf := &bytes.Buffer{} - if err := renderRBACYaml(buf, projectName, groupName); err != nil { - return err + td := tmplData{ + ProjectName: projectName, + GroupName: groupName, } - return writeFileAndPrint(filepath.Join(deployDir, rbacYaml), buf.Bytes(), defaultFileMode) + + return renderWriteFile(filepath.Join(deployDir, rbacYaml), rbacTmplName, rbacYamlTmpl, td) } func renderDeployFiles(deployDir, projectName, apiVersion, kind string) error { - buf := &bytes.Buffer{} - if err := renderRBACYaml(buf, projectName, groupName(apiVersion)); err != nil { - return err + rbacTd := tmplData{ + ProjectName: projectName, + GroupName: groupName(apiVersion), } - if err := writeFileAndPrint(filepath.Join(deployDir, rbacYaml), buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(filepath.Join(deployDir, rbacYaml), rbacTmplName, rbacYamlTmpl, rbacTd); err != nil { return err } - buf = &bytes.Buffer{} - if err := renderCRDYaml(buf, kind, apiVersion); err != nil { - return err + crdTd := tmplData{ + Kind: kind, + KindSingular: strings.ToLower(kind), + KindPlural: toPlural(strings.ToLower(kind)), + GroupName: groupName(apiVersion), + Version: version(apiVersion), } - if err := writeFileAndPrint(filepath.Join(deployDir, crdYaml), buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(filepath.Join(deployDir, crdYaml), crdTmplName, crdYamlTmpl, crdTd); err != nil { return err } - buf = &bytes.Buffer{} - if err := renderCustomResourceYaml(buf, apiVersion, kind); err != nil { - return err + crTd := tmplData{ + APIVersion: apiVersion, + Kind: kind, } - return writeFileAndPrint(filepath.Join(deployDir, crYaml), buf.Bytes(), defaultFileMode) + return renderWriteFile(filepath.Join(deployDir, crYaml), crTmplName, crYamlTmpl, crTd) } // RenderOperatorYaml generates "deploy/operator.yaml" func RenderOperatorYaml(c *Config, image string) error { - buf := &bytes.Buffer{} - if err := renderOperatorYaml(buf, c.ProjectName, image); err != nil { - return err + td := tmplData{ + ProjectName: c.ProjectName, + Image: image, + MetricsPort: k8sutil.PrometheusMetricsPort, + MetricsPortName: k8sutil.PrometheusMetricsPortName, } - return ioutil.WriteFile(operatorYaml, buf.Bytes(), defaultFileMode) + return renderWriteFile(operatorYaml, operatorTmplName, operatorYamlTmpl, td) } // RenderOlmCatalog generates catalog manifests "deploy/olm-catalog/*" @@ -245,71 +265,77 @@ func RenderOlmCatalog(c *Config, image, version string) error { return err } olmDir := filepath.Join(repoPath, olmCatalogDir) - if err := os.MkdirAll(olmDir, defaultDirFileMode); err != nil { - return err - } // deploy/olm-catalog/package.yaml - buf := &bytes.Buffer{} - if err := renderCatalogPackage(buf, c, version); err != nil { - return err + cpTd := tmplData{ + PackageName: strings.ToLower(c.Kind), + ChannelName: packageChannel, + CurrentCSV: getCSVName(strings.ToLower(c.Kind), version), } path := filepath.Join(olmDir, catalogPackageYaml) - if err := ioutil.WriteFile(path, buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(path, catalogPackageYaml, catalogPackageTmpl, cpTd); err != nil { return err } // deploy/olm-catalog/crd.yaml - buf = &bytes.Buffer{} - if err := renderCatalogCRD(buf, c); err != nil { - return err + ccrdTd := tmplData{ + Kind: c.Kind, + KindSingular: strings.ToLower(c.Kind), + KindPlural: toPlural(strings.ToLower(c.Kind)), + GroupName: groupName(c.APIVersion), + Version: version, } path = filepath.Join(olmDir, crdYaml) - if err := ioutil.WriteFile(path, buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(path, catalogCRDTmplName, crdTmpl, ccrdTd); err != nil { return err } // deploy/olm-catalog/csv.yaml - buf = &bytes.Buffer{} - if err := renderCatalogCSV(buf, c, image, version); err != nil { - return err + ccsvTd := tmplData{ + Kind: c.Kind, + KindSingular: strings.ToLower(c.Kind), + KindPlural: toPlural(strings.ToLower(c.Kind)), + GroupName: groupName(c.APIVersion), + CRDVersion: version, + CSVName: getCSVName(strings.ToLower(c.Kind), version), + Image: image, + CatalogVersion: version, + ProjectName: c.ProjectName, } path = filepath.Join(olmDir, catalogCSVYaml) - return ioutil.WriteFile(path, buf.Bytes(), defaultFileMode) + return renderWriteFile(path, catalogCSVYaml, catalogCSVTmpl, ccsvTd) +} + +func getCSVName(name, version string) string { + return name + ".v" + version } func (g *Generator) renderTmp() error { bDir := filepath.Join(g.projectName, buildDir) - if err := os.MkdirAll(bDir, defaultDirFileMode); err != nil { - return err - } if err := renderBuildFiles(bDir, g.repoPath, g.projectName); err != nil { return err } cDir := filepath.Join(g.projectName, codegenDir) - if err := os.MkdirAll(cDir, defaultDirFileMode); err != nil { - return err - } return renderCodegenFiles(cDir, g.repoPath, apiDirName(g.apiVersion), version(g.apiVersion), g.projectName) } func (g *Generator) renderVersion() error { - buf := &bytes.Buffer{} - - if err := os.MkdirAll(filepath.Join(g.projectName, versionDir), defaultDirFileMode); err != nil { - return err + td := tmplData{ + VersionNumber: "0.0.1", } - if err := renderVersionFile(buf, "0.0.1"); err != nil { - return err - } - return writeFileAndPrint(filepath.Join(g.projectName, versionDir, versionfile), buf.Bytes(), defaultFileMode) + return renderWriteFile(filepath.Join(g.projectName, versionDir, versionfile), "version/version.go", versionTmpl, td) } func renderBuildFiles(buildDir, repoPath, projectName string) error { buf := &bytes.Buffer{} - if err := renderBuildFile(buf, repoPath, projectName); err != nil { + bTd := tmplData{ + ProjectName: projectName, + RepoPath: repoPath, + } + + if err := renderFile(buf, "tmp/build/build.sh", buildTmpl, bTd); err != nil { return err } if err := writeFileAndPrint(filepath.Join(buildDir, build), buf.Bytes(), defaultExecFileMode); err != nil { @@ -324,24 +350,35 @@ func renderBuildFiles(buildDir, repoPath, projectName string) error { return err } - buf = &bytes.Buffer{} - if err := renderDockerFile(buf, projectName); err != nil { + dTd := tmplData{ + ProjectName: projectName, + } + if err := renderFile(buf, "tmp/build/Dockerfile", dockerFileTmpl, dTd); err != nil { return err } - return writeFileAndPrint(filepath.Join(buildDir, dockerfile), buf.Bytes(), defaultFileMode) + return renderWriteFile(filepath.Join(buildDir, dockerfile), "tmp/build/Dockerfile", dockerFileTmpl, dTd) +} + +func renderDockerBuildFile(w io.Writer) error { + _, err := w.Write([]byte(dockerBuildTmpl)) + return err } func renderCodegenFiles(codegenDir, repoPath, apiDirName, version, projectName string) error { - buf := &bytes.Buffer{} - if err := renderBoilerplateFile(buf, projectName); err != nil { - return err + bTd := tmplData{ + ProjectName: projectName, } - if err := writeFileAndPrint(filepath.Join(codegenDir, boilerplate), buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(filepath.Join(codegenDir, boilerplate), "codegen/boilerplate.go.txt", boilerplateTmpl, bTd); err != nil { return err } - buf = &bytes.Buffer{} - if err := renderUpdateGeneratedFile(buf, repoPath, apiDirName, version); err != nil { + buf := &bytes.Buffer{} + ugTd := tmplData{ + RepoPath: repoPath, + APIDirName: apiDirName, + Version: version, + } + if err := renderFile(buf, "codegen/update-generated.sh", updateGeneratedTmpl, ugTd); err != nil { return err } return writeFileAndPrint(filepath.Join(codegenDir, updateGenerated), buf.Bytes(), defaultExecFileMode) @@ -351,50 +388,123 @@ func (g *Generator) renderPkg() error { v := version(g.apiVersion) adn := apiDirName(g.apiVersion) apiDir := filepath.Join(g.projectName, apisDir, adn, v) - if err := os.MkdirAll(apiDir, defaultDirFileMode); err != nil { - return err - } if err := renderAPIFiles(apiDir, groupName(g.apiVersion), v, g.kind); err != nil { return err } sDir := filepath.Join(g.projectName, stubDir) - if err := os.MkdirAll(sDir, defaultDirFileMode); err != nil { - return err - } return renderStubFiles(sDir, g.repoPath, g.kind, adn, v) } func renderAPIFiles(apiDir, groupName, version, kind string) error { - buf := &bytes.Buffer{} - if err := renderAPIDocFile(buf, groupName, version); err != nil { - return err + adTd := tmplData{ + GroupName: groupName, + Version: version, } - if err := writeFileAndPrint(filepath.Join(apiDir, doc), buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(filepath.Join(apiDir, doc), "apis///doc.go", apiDocTmpl, adTd); err != nil { return err } - buf = &bytes.Buffer{} - if err := renderAPIRegisterFile(buf, kind, groupName, version); err != nil { - return err + arTd := tmplData{ + Kind: kind, + KindPlural: toPlural(strings.ToLower(kind)), + GroupName: groupName, + Version: version, } - if err := writeFileAndPrint(filepath.Join(apiDir, register), buf.Bytes(), defaultFileMode); err != nil { + if err := renderWriteFile(filepath.Join(apiDir, register), "apis///register.go", apiRegisterTmpl, arTd); err != nil { return err } - buf = &bytes.Buffer{} - if err := renderAPITypesFile(buf, kind, version); err != nil { - return err + atTd := tmplData{ + Kind: kind, + Version: version, } - return writeFileAndPrint(filepath.Join(apiDir, types), buf.Bytes(), defaultFileMode) + return renderWriteFile(filepath.Join(apiDir, types), "apis///types.go", apiTypesTmpl, atTd) } func renderStubFiles(stubDir, repoPath, kind, apiDirName, version string) error { - buf := &bytes.Buffer{} - if err := renderHandlerFile(buf, repoPath, kind, apiDirName, version); err != nil { + td := tmplData{ + OperatorSDKImport: sdkImport, + RepoPath: repoPath, + Kind: kind, + APIDirName: apiDirName, + Version: version, + } + return renderWriteFile(filepath.Join(stubDir, handler), "stub/handler.go", handlerTmpl, td) +} + +type tmplData struct { + VersionNumber string + + OperatorSDKImport string + StubImport string + K8sutilImport string + SDKVersionImport string + + MetricsPort int + MetricsPortName string + + APIVersion string + Kind string + + RepoPath string + APIDirName string + Version string + + ProjectName string + GroupName string + + // singular name to be used as an alias on the CLI and for display + KindSingular string + // plural name to be used in the URL: /apis/// + KindPlural string + + Image string + Name string + + PackageName string + ChannelName string + CurrentCSV string + + CRDVersion string + CSVName string + CatalogVersion string +} + +// Creates all the necesary directories for the generated files +func (g *Generator) generateDirStructure() error { + dirsToCreate := []string{ + g.projectName, + filepath.Join(g.projectName, cmdDir, g.projectName), + filepath.Join(g.projectName, configDir), + filepath.Join(g.projectName, deployDir), + filepath.Join(g.projectName, olmCatalogDir), + filepath.Join(g.projectName, buildDir), + filepath.Join(g.projectName, codegenDir), + filepath.Join(g.projectName, versionDir), + filepath.Join(g.projectName, apisDir, apiDirName(g.apiVersion), version(g.apiVersion)), + filepath.Join(g.projectName, stubDir), + } + + for _, dir := range dirsToCreate { + if err := os.MkdirAll(dir, defaultDirFileMode); err != nil { + return err + } + } + + return nil +} + +// Renders a file given a template, and fills in template fields according to values passed in the tmplData struct +func renderFile(w io.Writer, fileLoc string, fileTmpl string, info tmplData) error { + t := template.New(fileLoc) + + t, err := t.Parse(fileTmpl) + if err != nil { return err } - return writeFileAndPrint(filepath.Join(stubDir, handler), buf.Bytes(), defaultFileMode) + + return t.Execute(w, info) } // version extracts the VERSION from the given apiVersion ($GROUP_NAME/$VERSION). @@ -416,6 +526,7 @@ func apiDirName(apiVersion string) string { return strings.Split(groupName(apiVersion), ".")[0] } +// Writes file to a given path and data buffer, as well as prints out a message confirming creation of a file func writeFileAndPrint(filePath string, data []byte, fileMode os.FileMode) error { if err := ioutil.WriteFile(filePath, data, fileMode); err != nil { return err @@ -423,3 +534,18 @@ func writeFileAndPrint(filePath string, data []byte, fileMode os.FileMode) error fmt.Printf("Create %v \n", filePath) return nil } + +// Combines steps of creating buffer, writing to buffer, and writing buffer to file in one call +func renderWriteFile(filePath string, fileLoc string, fileTmpl string, info tmplData) error { + buf := &bytes.Buffer{} + + if err := renderFile(buf, fileLoc, fileTmpl, info); err != nil { + return err + } + + if err := writeFileAndPrint(filePath, buf.Bytes(), defaultFileMode); err != nil { + return err + } + + return nil +} diff --git a/pkg/generator/gen_version_test.go b/pkg/generator/generator_test.go similarity index 89% rename from pkg/generator/gen_version_test.go rename to pkg/generator/generator_test.go index 5c2ef198f0..3d18d68e98 100644 --- a/pkg/generator/gen_version_test.go +++ b/pkg/generator/generator_test.go @@ -28,7 +28,7 @@ var ( func TestGenVersion(t *testing.T) { buf := &bytes.Buffer{} - if err := renderVersionFile(buf, "0.9.2+git"); err != nil { + if err := renderFile(buf, "version/version.go", versionTmpl, tmplData{VersionNumber: "0.9.2+git"}); err != nil { t.Error(err) return } diff --git a/pkg/generator/templates.go b/pkg/generator/templates.go index cdfe30a380..dba844c379 100644 --- a/pkg/generator/templates.go +++ b/pkg/generator/templates.go @@ -130,37 +130,57 @@ spec: const mainTmpl = `package main import ( - "context" - "runtime" + "context" + "runtime" + "net/http" - stub "{{.StubImport}}" - sdk "{{.OperatorSDKImport}}" - k8sutil "{{.K8sutilImport}}" - sdkVersion "{{.SDKVersionImport}}" + stub "{{.StubImport}}" + sdk "{{.OperatorSDKImport}}" + k8sutil "{{.K8sutilImport}}" + sdkVersion "{{.SDKVersionImport}}" - "github.com/sirupsen/logrus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" ) func printVersion() { - logrus.Infof("Go Version: %s", runtime.Version()) - logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) - logrus.Infof("operator-sdk Version: %v", sdkVersion.Version) + logrus.Infof("Go Version: %s", runtime.Version()) + logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH) + logrus.Infof("operator-sdk Version: %v", sdkVersion.Version) + logrus.Infof("operator prometheus port %d", {{.MetricsPort}}) } -func main() { - printVersion() +func initOperatorService() { + service, err := k8sutil.InitOperatorService() + if err != nil { + logrus.Fatalf("Failed to init operator service: %v", err) + } + err = sdk.Create(service) + if err != nil && !errors.IsAlreadyExists(err) { + logrus.Infof("Failed to create operator service: %v", err) + return + } + logrus.Infof("Metrics service %s created", service.Name) +} - resource := "{{.APIVersion}}" - kind := "{{.Kind}}" - namespace, err := k8sutil.GetWatchNamespace() - if err != nil { - logrus.Fatalf("Failed to get watch namespace: %v", err) - } - resyncPeriod := 5 - logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod) - sdk.Watch(resource, kind, namespace, resyncPeriod) - sdk.Handle(stub.NewHandler()) - sdk.Run(context.TODO()) +func main() { + printVersion() + initOperatorService() + + http.Handle("/metrics", promhttp.Handler()) + go http.ListenAndServe(":{{.MetricsPort}}", nil) + + resource := "{{.APIVersion}}" + kind := "{{.Kind}}" + namespace, err := k8sutil.GetWatchNamespace() + if err != nil { + logrus.Fatalf("Failed to get watch namespace: %v", err) + } + resyncPeriod := 5 + logrus.Infof("Watching %s, %s, %s, %d", resource, kind, namespace, resyncPeriod) + sdk.Watch(resource, kind, namespace, resyncPeriod) + sdk.Handle(stub.NewHandler()) + sdk.Run(context.TODO()) } ` @@ -379,7 +399,11 @@ const gopkgTomlTmpl = `[[override]] [[override]] name = "k8s.io/client-go" - version = "kubernetes-1.9.3" + version = "kubernetes-1.9.3" + +[[override]] + name = "github.com/prometheus/client_golang" + version = "0.8.0" [[constraint]] name = "github.com/operator-framework/operator-sdk" @@ -516,6 +540,8 @@ const operatorYamlTmpl = `apiVersion: apps/v1 kind: Deployment metadata: name: {{.ProjectName}} + labels: + name: {{.ProjectName}} spec: replicas: 1 selector: @@ -529,14 +555,19 @@ spec: containers: - name: {{.ProjectName}} image: {{.Image}} + ports: + - containerPort: {{.MetricsPort}} + name: {{.MetricsPortName}} command: - {{.ProjectName}} imagePullPolicy: Always env: - - name: WATCH_NAMESPACE + - name: {{.NamespaceEnv}} valueFrom: fieldRef: fieldPath: metadata.namespace + - name: {{.NameEnv}} + value: "{{.ProjectName}}" ` const rbacYamlTmpl = `kind: Role diff --git a/pkg/util/k8sutil/constants.go b/pkg/util/k8sutil/constants.go index fd67036cca..f447c5344d 100644 --- a/pkg/util/k8sutil/constants.go +++ b/pkg/util/k8sutil/constants.go @@ -8,4 +8,14 @@ const ( // WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE // which is the namespace that the pod is currently running in. WatchNamespaceEnvVar = "WATCH_NAMESPACE" + + // OperatorName is the constant for env variable OPERATOR_NAME + // wich is the name of the current operator + OperatorNameEnvVar = "OPERATOR_NAME" + + // PrometheusMetricsPort defines the port which expose prometheus metrics + PrometheusMetricsPort = 60000 + + // PrometheusMetricsPortName define the port name used in kubernetes deployment and service + PrometheusMetricsPortName = "metrics" ) diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index f317f67bb5..ae8eb143f5 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -19,12 +19,14 @@ import ( "fmt" "os" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" + intstr "k8s.io/apimachinery/pkg/util/intstr" cgoscheme "k8s.io/client-go/kubernetes/scheme" ) @@ -147,3 +149,53 @@ func GetWatchNamespace() (string, error) { } return ns, nil } + +// GetOperatorName return the operator name +func GetOperatorName() (string, error) { + operatorName, found := os.LookupEnv(OperatorNameEnvVar) + if !found { + return "", fmt.Errorf("%s must be set", OperatorNameEnvVar) + } + if len(operatorName) == 0 { + return "", fmt.Errorf("%s must not be empty", OperatorNameEnvVar) + } + return operatorName, nil +} + +// InitOperatorService return the static service which expose operator metrics +func InitOperatorService() (*v1.Service, error) { + operatorName, err := GetOperatorName() + if err != nil { + return nil, err + } + namespace, err := GetWatchNamespace() + if err != nil { + return nil, err + } + service := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: operatorName, + Namespace: namespace, + Labels: map[string]string{"name": operatorName}, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + { + Port: PrometheusMetricsPort, + Protocol: v1.ProtocolTCP, + TargetPort: intstr.IntOrString{ + Type: intstr.String, + StrVal: PrometheusMetricsPortName, + }, + Name: PrometheusMetricsPortName, + }, + }, + Selector: map[string]string{"name": operatorName}, + }, + } + return service, nil +} diff --git a/pkg/util/k8sutil/k8sutil_test.go b/pkg/util/k8sutil/k8sutil_test.go new file mode 100644 index 0000000000..0e5c257874 --- /dev/null +++ b/pkg/util/k8sutil/k8sutil_test.go @@ -0,0 +1,61 @@ +package k8sutil + +import ( + "fmt" + "os" + "reflect" + "testing" +) + +func TestGetOperatorName(t *testing.T) { + type Output struct { + operatorName string + err error + } + + type Scenario struct { + name string + envVarKey string + envVarValue string + expectedOutput Output + } + + tests := []Scenario{ + Scenario{ + name: "Simple case", + envVarKey: OperatorNameEnvVar, + envVarValue: "myoperator", + expectedOutput: Output{ + operatorName: "myoperator", + err: nil, + }, + }, + Scenario{ + name: "Unset env var", + envVarKey: "", + envVarValue: "", + expectedOutput: Output{ + operatorName: "", + err: fmt.Errorf("%s must be set", OperatorNameEnvVar), + }, + }, + Scenario{ + name: "Empty env var", + envVarKey: OperatorNameEnvVar, + envVarValue: "", + expectedOutput: Output{ + operatorName: "", + err: fmt.Errorf("%s must not be empty", OperatorNameEnvVar), + }, + }, + } + + for _, test := range tests { + _ = os.Setenv(test.envVarKey, test.envVarValue) + operatorName, err := GetOperatorName() + if !(operatorName == test.expectedOutput.operatorName && reflect.DeepEqual(err, test.expectedOutput.err)) { + t.Errorf("test %s failed, expected ouput: %s,%v; got: %s,%v", test.name, test.expectedOutput.operatorName, test.expectedOutput.err, operatorName, err) + } + _ = os.Unsetenv(test.envVarKey) + } +}