Init kubebuilder tutorial

This commit is contained in:
Nicolas CARON 2024-06-07 13:44:17 +02:00
parent 795d6f256c
commit 1da5f03e8e
21 changed files with 6 additions and 652 deletions

14
PROJECT
View File

@ -2,19 +2,9 @@
# This file is used to track the info used to scaffold your project # This file is used to track the info used to scaffold your project
# and allow the plugins properly work. # and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html # More info: https://book.kubebuilder.io/reference/project-config.html
domain: localhost domain: tutorial.kubebuilder.io
layout: layout:
- go.kubebuilder.io/v4 - go.kubebuilder.io/v4
projectName: operator-test projectName: operator-test
repo: git.coopgo.io/ncaron/operator-test repo: tutorial.kubebuilder.io/project
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: localhost
group: webapp
kind: Operator
path: git.coopgo.io/ncaron/operator-test/api/v1
version: v1
version: "3" version: "3"

View File

@ -1,36 +0,0 @@
/*
Copyright 2024.
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 v1 contains API Schema definitions for the webapp v1 API group
// +kubebuilder:object:generate=true
// +groupName=webapp.localhost
package v1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "webapp.localhost", Version: "v1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@ -1,64 +0,0 @@
/*
Copyright 2024.
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 v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// OperatorSpec defines the desired state of Operator
type OperatorSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of Operator. Edit operator_types.go to remove/update
Foo string `json:"foo,omitempty"`
}
// OperatorStatus defines the observed state of Operator
type OperatorStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// Operator is the Schema for the operators API
type Operator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec OperatorSpec `json:"spec,omitempty"`
Status OperatorStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// OperatorList contains a list of Operator
type OperatorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Operator `json:"items"`
}
func init() {
SchemeBuilder.Register(&Operator{}, &OperatorList{})
}

View File

@ -1,114 +0,0 @@
//go:build !ignore_autogenerated
/*
Copyright 2024.
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.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Operator) DeepCopyInto(out *Operator) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Operator.
func (in *Operator) DeepCopy() *Operator {
if in == nil {
return nil
}
out := new(Operator)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Operator) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorList) DeepCopyInto(out *OperatorList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Operator, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorList.
func (in *OperatorList) DeepCopy() *OperatorList {
if in == nil {
return nil
}
out := new(OperatorList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OperatorList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorSpec) DeepCopyInto(out *OperatorSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorSpec.
func (in *OperatorSpec) DeepCopy() *OperatorSpec {
if in == nil {
return nil
}
out := new(OperatorSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorStatus) DeepCopyInto(out *OperatorStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorStatus.
func (in *OperatorStatus) DeepCopy() *OperatorStatus {
if in == nil {
return nil
}
out := new(OperatorStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -33,9 +33,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook"
webappv1 "git.coopgo.io/ncaron/operator-test/api/v1"
"git.coopgo.io/ncaron/operator-test/internal/controller"
// +kubebuilder:scaffold:imports // +kubebuilder:scaffold:imports
) )
@ -47,7 +44,6 @@ var (
func init() { func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(webappv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme // +kubebuilder:scaffold:scheme
} }
@ -105,7 +101,7 @@ func main() {
WebhookServer: webhookServer, WebhookServer: webhookServer,
HealthProbeBindAddress: probeAddr, HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection, LeaderElection: enableLeaderElection,
LeaderElectionID: "cc89aa66.localhost", LeaderElectionID: "80807133.tutorial.kubebuilder.io",
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
// when the Manager ends. This requires the binary to immediately end when the // when the Manager ends. This requires the binary to immediately end when the
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
@ -123,13 +119,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if err = (&controller.OperatorReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Operator")
os.Exit(1)
}
// +kubebuilder:scaffold:builder // +kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

View File

@ -1,24 +0,0 @@
# This kustomization.yaml is not intended to be run by itself,
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/webapp.localhost_operator-tests.yaml
- bases/webapp.localhost_operators.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patches:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- path: patches/cainjection_in_operator-tests.yaml
#- path: patches/cainjection_in_operators.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# [WEBHOOK] To enable webhook, uncomment the following section
# the following config is for teaching kustomize how to do kustomization for CRDs.
#configurations:
#- kustomizeconfig.yaml

View File

@ -1,19 +0,0 @@
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/name
namespace:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/namespace
create: false
varReference:
- path: metadata/annotations

View File

@ -15,7 +15,7 @@ namePrefix: operator-test-
# someName: someValue # someName: someValue
resources: resources:
- ../crd #- ../crd
- ../rbac - ../rbac
- ../manager - ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in

View File

@ -9,12 +9,3 @@ resources:
- role_binding.yaml - role_binding.yaml
- leader_election_role.yaml - leader_election_role.yaml
- leader_election_role_binding.yaml - leader_election_role_binding.yaml
# For each CRD, "Editor" and "Viewer" roles are scaffolded by
# default, aiding admins in cluster management. Those roles are
# not used by the Project itself. You can comment the following lines
# if you do not want those helpers be installed with your Project.
- operator_editor_role.yaml
- operator_viewer_role.yaml
- operator-test_editor_role.yaml
- operator-test_viewer_role.yaml

View File

@ -1,27 +0,0 @@
# permissions for end users to edit operator-tests.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-test-editor-role
rules:
- apiGroups:
- webapp.localhost
resources:
- operator-tests
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- webapp.localhost
resources:
- operator-tests/status
verbs:
- get

View File

@ -1,23 +0,0 @@
# permissions for end users to view operator-tests.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-test-viewer-role
rules:
- apiGroups:
- webapp.localhost
resources:
- operator-tests
verbs:
- get
- list
- watch
- apiGroups:
- webapp.localhost
resources:
- operator-tests/status
verbs:
- get

View File

@ -1,27 +0,0 @@
# permissions for end users to edit operators.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-editor-role
rules:
- apiGroups:
- webapp.localhost
resources:
- operators
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- webapp.localhost
resources:
- operators/status
verbs:
- get

View File

@ -1,23 +0,0 @@
# permissions for end users to view operators.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-viewer-role
rules:
- apiGroups:
- webapp.localhost
resources:
- operators
verbs:
- get
- list
- watch
- apiGroups:
- webapp.localhost
resources:
- operators/status
verbs:
- get

View File

@ -1,5 +0,0 @@
## Append samples of your project ##
resources:
- webapp_v1_operator-test.yaml
- webapp_v1_operator.yaml
# +kubebuilder:scaffold:manifestskustomizesamples

View File

@ -1,9 +0,0 @@
apiVersion: webapp.localhost/v1
kind: Operator-test
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-test-sample
spec:
# TODO(user): Add fields here

View File

@ -1,9 +0,0 @@
apiVersion: webapp.localhost/v1
kind: Operator
metadata:
labels:
app.kubernetes.io/name: operator-test
app.kubernetes.io/managed-by: kustomize
name: operator-sample
spec:
# TODO(user): Add fields here

2
go.mod
View File

@ -1,4 +1,4 @@
module git.coopgo.io/ncaron/operator-test module tutorial.kubebuilder.io/project
go 1.22.0 go 1.22.0

View File

@ -1,62 +0,0 @@
/*
Copyright 2024.
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 controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
webappv1 "git.coopgo.io/ncaron/operator-test/api/v1"
)
// OperatorReconciler reconciles a Operator object
type OperatorReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=webapp.localhost,resources=operators,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=webapp.localhost,resources=operators/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=webapp.localhost,resources=operators/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Operator object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.2/pkg/reconcile
func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&webappv1.Operator{}).
Complete(r)
}

View File

@ -1,84 +0,0 @@
/*
Copyright 2024.
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 controller
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
webappv1 "git.coopgo.io/ncaron/operator-test/api/v1"
)
var _ = Describe("Operator Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
operator := &webappv1.Operator{}
BeforeEach(func() {
By("creating the custom resource for the Kind Operator")
err := k8sClient.Get(ctx, typeNamespacedName, operator)
if err != nil && errors.IsNotFound(err) {
resource := &webappv1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &webappv1.Operator{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance Operator")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &OperatorReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

View File

@ -1,90 +0,0 @@
/*
Copyright 2024.
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 controller
import (
"fmt"
"path/filepath"
"runtime"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
webappv1 "git.coopgo.io/ncaron/operator-test/api/v1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
// The BinaryAssetsDirectory is only required if you want to run the tests directly
// without call the makefile target test. If not informed it will look for the
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
// Note that you must have the required binaries setup under the bin directory to perform
// the tests directly. When we run make test it will be setup and used automatically.
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
fmt.Sprintf("1.30.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
}
var err error
// cfg is defined in this file globally.
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
err = webappv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

View File

@ -24,7 +24,7 @@ import (
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"git.coopgo.io/ncaron/operator-test/test/utils" "tutorial.kubebuilder.io/project/test/utils"
) )
const namespace = "operator-test-system" const namespace = "operator-test-system"