Skip to content

Commit

Permalink
Improve and Simplify the Getting Started Guide
Browse files Browse the repository at this point in the history
- Refined the sample code implementation to keep it minimal, helping users better understand the core concepts.
- Streamlined the guide by focusing on the main information and removing extraneous details.
- Ensured that the code and samples in the guide are consistent with the generated project.
- Simplified navigation by hiding the full code, making it easier for users to follow along.
- Replaced the use of the deploy image plugin with direct commands to provide more comprehensive and straightforward information.
  • Loading branch information
camilamacedo86 committed Sep 3, 2024
1 parent d01d487 commit 43690b1
Show file tree
Hide file tree
Showing 10 changed files with 679 additions and 633 deletions.
559 changes: 220 additions & 339 deletions docs/book/src/getting-started.md

Large diffs are not rendered by default.

12 changes: 0 additions & 12 deletions docs/book/src/getting-started/testdata/project/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,6 @@
domain: example.com
layout:
- go.kubebuilder.io/v4
plugins:
deploy-image.go.kubebuilder.io/v1-alpha:
resources:
- domain: example.com
group: cache
kind: Memcached
options:
containerCommand: memcached,-m=64,-o,modern,-v
containerPort: "11211"
image: memcached:1.4.36-alpine
runAsUser: "1001"
version: v1alpha1
projectName: project
repo: example.com/memcached
resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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.
*/
// +kubebuilder:docs-gen:collapse=Apache License

package v1alpha1

Expand All @@ -23,6 +24,8 @@ import (
// 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.

// +kubebuilder:docs-gen:collapse=Imports

// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Expand All @@ -35,9 +38,6 @@ type MemcachedSpec struct {
// +kubebuilder:validation:Maximum=3
// +kubebuilder:validation:ExclusiveMaximum=false
Size int32 `json:"size,omitempty"`

// Port defines the port that will be used to init the container with the image
ContainerPort int32 `json:"containerPort,omitempty"`
}

// MemcachedStatus defines the observed state of Memcached
Expand Down
5 changes: 2 additions & 3 deletions docs/book/src/getting-started/testdata/project/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ func main() {
}

if err = (&controller.MemcachedReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("memcached-controller"),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Memcached")
os.Exit(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ spec:
spec:
description: MemcachedSpec defines the desired state of Memcached
properties:
containerPort:
description: Port defines the port that will be used to init the container
with the image
format: int32
type: integer
size:
description: |-
Size defines the number of Memcached instances
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ spec:
- --health-probe-bind-address=:8081
image: controller:latest
name: manager
env:
- name: MEMCACHED_IMAGE
value: memcached:1.4.36-alpine
securityContext:
allowPrivilegeEscalation: false
capabilities:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,3 @@ spec:
# TODO(user): edit the following value to ensure the number
# of Pods/Instances your Operand must have on cluster
size: 1

# TODO(user): edit the following value to ensure the container has the right port to be initialized
containerPort: 11211
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,22 @@ package controller
import (
"context"
"fmt"
"os"
"strings"
"time"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"time"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"

cachev1alpha1 "example.com/memcached/api/v1alpha1"
)

const memcachedFinalizer = "cache.example.com/finalizer"

// Definitions to manage status conditions
const (
// typeAvailableMemcached represents the status of the Deployment reconciliation
Expand All @@ -52,14 +46,9 @@ const (
// MemcachedReconciler reconciles a Memcached object
type MemcachedReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
Scheme *runtime.Scheme
}

// The following markers are used to generate the rules permissions (RBAC) on config/rbac using controller-gen
// when the command <make manifests> is executed.
// To know more about markers see: https://book.kubebuilder.io/reference/markers.html

// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
Expand All @@ -77,6 +66,8 @@ type MemcachedReconciler struct {
// For further info:
// - About Operator Pattern: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
// - About Controllers: https://kubernetes.io/docs/concepts/architecture/controller/
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
Expand Down Expand Up @@ -117,79 +108,6 @@ func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}
}

// Let's add a finalizer. Then, we can define some operations which should
// occur before the custom resource is deleted.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers
if !controllerutil.ContainsFinalizer(memcached, memcachedFinalizer) {
log.Info("Adding Finalizer for Memcached")
if ok := controllerutil.AddFinalizer(memcached, memcachedFinalizer); !ok {
log.Error(err, "Failed to add finalizer into the custom resource")
return ctrl.Result{Requeue: true}, nil
}

if err = r.Update(ctx, memcached); err != nil {
log.Error(err, "Failed to update custom resource to add finalizer")
return ctrl.Result{}, err
}
}

// Check if the Memcached instance is marked to be deleted, which is
// indicated by the deletion timestamp being set.
isMemcachedMarkedToBeDeleted := memcached.GetDeletionTimestamp() != nil
if isMemcachedMarkedToBeDeleted {
if controllerutil.ContainsFinalizer(memcached, memcachedFinalizer) {
log.Info("Performing Finalizer Operations for Memcached before delete CR")

// Let's add here a status "Downgrade" to reflect that this resource began its process to be terminated.
meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeDegradedMemcached,
Status: metav1.ConditionUnknown, Reason: "Finalizing",
Message: fmt.Sprintf("Performing finalizer operations for the custom resource: %s ", memcached.Name)})

if err := r.Status().Update(ctx, memcached); err != nil {
log.Error(err, "Failed to update Memcached status")
return ctrl.Result{}, err
}

// Perform all operations required before removing the finalizer and allow
// the Kubernetes API to remove the custom resource.
r.doFinalizerOperationsForMemcached(memcached)

// TODO(user): If you add operations to the doFinalizerOperationsForMemcached method
// then you need to ensure that all worked fine before deleting and updating the Downgrade status
// otherwise, you should requeue here.

// Re-fetch the memcached Custom Resource before updating the status
// so that we have the latest state of the resource on the cluster and we will avoid
// raising the error "the object has been modified, please apply
// your changes to the latest version and try again" which would re-trigger the reconciliation
if err := r.Get(ctx, req.NamespacedName, memcached); err != nil {
log.Error(err, "Failed to re-fetch memcached")
return ctrl.Result{}, err
}

meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeDegradedMemcached,
Status: metav1.ConditionTrue, Reason: "Finalizing",
Message: fmt.Sprintf("Finalizer operations for custom resource %s name were successfully accomplished", memcached.Name)})

if err := r.Status().Update(ctx, memcached); err != nil {
log.Error(err, "Failed to update Memcached status")
return ctrl.Result{}, err
}

log.Info("Removing Finalizer for Memcached after successfully perform the operations")
if ok := controllerutil.RemoveFinalizer(memcached, memcachedFinalizer); !ok {
log.Error(err, "Failed to remove finalizer for Memcached")
return ctrl.Result{Requeue: true}, nil
}

if err := r.Update(ctx, memcached); err != nil {
log.Error(err, "Failed to remove finalizer for Memcached")
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}

// Check if the deployment already exists, if not create a new one
found := &appsv1.Deployment{}
err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
Expand Down Expand Up @@ -282,37 +200,19 @@ func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, nil
}

// finalizeMemcached will perform the required operations before delete the CR.
func (r *MemcachedReconciler) doFinalizerOperationsForMemcached(cr *cachev1alpha1.Memcached) {
// TODO(user): Add the cleanup steps that the operator
// needs to do before the CR can be deleted. Examples
// of finalizers include performing backups and deleting
// resources that are not owned by this CR, like a PVC.

// Note: It is not recommended to use finalizers with the purpose of deleting resources which are
// created and managed in the reconciliation. These ones, such as the Deployment created on this reconcile,
// are defined as dependent of the custom resource. See that we use the method ctrl.SetControllerReference.
// to set the ownerRef which means that the Deployment will be deleted by the Kubernetes API.
// More info: https://kubernetes.io/docs/tasks/administer-cluster/use-cascading-deletion/

// The following implementation will raise an event
r.Recorder.Event(cr, "Warning", "Deleting",
fmt.Sprintf("Custom Resource %s is being deleted from the namespace %s",
cr.Name,
cr.Namespace))
// SetupWithManager sets up the controller with the Manager.
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cachev1alpha1.Memcached{}).
Owns(&appsv1.Deployment{}).
Complete(r)
}

// deploymentForMemcached returns a Memcached Deployment object
func (r *MemcachedReconciler) deploymentForMemcached(
memcached *cachev1alpha1.Memcached) (*appsv1.Deployment, error) {
ls := labelsForMemcached()
replicas := memcached.Spec.Size

// Get the Operand image
image, err := imageForMemcached()
if err != nil {
return nil, err
}
image := "memcached:1.6.26-alpine3.19"

dep := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -322,46 +222,15 @@ func (r *MemcachedReconciler) deploymentForMemcached(
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
MatchLabels: map[string]string{"app.kubernetes.io/name": "project"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
Labels: map[string]string{"app.kubernetes.io/name": "project"},
},
Spec: corev1.PodSpec{
// TODO(user): Uncomment the following code to configure the nodeAffinity expression
// according to the platforms which are supported by your solution. It is considered
// best practice to support multiple architectures. build your manager image using the
// makefile target docker-buildx. Also, you can use docker manifest inspect <image>
// to check what are the platforms supported.
// More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
// Affinity: &corev1.Affinity{
// NodeAffinity: &corev1.NodeAffinity{
// RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
// NodeSelectorTerms: []corev1.NodeSelectorTerm{
// {
// MatchExpressions: []corev1.NodeSelectorRequirement{
// {
// Key: "kubernetes.io/arch",
// Operator: "In",
// Values: []string{"amd64", "arm64", "ppc64le", "s390x"},
// },
// {
// Key: "kubernetes.io/os",
// Operator: "In",
// Values: []string{"linux"},
// },
// },
// },
// },
// },
// },
// },
SecurityContext: &corev1.PodSecurityContext{
RunAsNonRoot: &[]bool{true}[0],
// IMPORTANT: seccomProfile was introduced with Kubernetes 1.19
// If you are looking for to produce solutions to be supported
// on lower versions you must remove this option.
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
Expand All @@ -383,7 +252,7 @@ func (r *MemcachedReconciler) deploymentForMemcached(
},
},
Ports: []corev1.ContainerPort{{
ContainerPort: memcached.Spec.ContainerPort,
ContainerPort: 11211,
Name: "memcached",
}},
Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
Expand All @@ -400,49 +269,3 @@ func (r *MemcachedReconciler) deploymentForMemcached(
}
return dep, nil
}

// labelsForMemcached returns the labels for selecting the resources
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/
func labelsForMemcached() map[string]string {
var imageTag string
image, err := imageForMemcached()
if err == nil {
imageTag = strings.Split(image, ":")[1]
}
return map[string]string{"app.kubernetes.io/name": "project",
"app.kubernetes.io/version": imageTag,
"app.kubernetes.io/managed-by": "MemcachedController",
}
}

// imageForMemcached gets the Operand image which is managed by this controller
// from the MEMCACHED_IMAGE environment variable defined in the config/manager/manager.yaml
func imageForMemcached() (string, error) {
var imageEnvVar = "MEMCACHED_IMAGE"
image, found := os.LookupEnv(imageEnvVar)
if !found {
return "", fmt.Errorf("Unable to find %s environment variable with the image", imageEnvVar)
}
return image, nil
}

// SetupWithManager sets up the controller with the Manager.
// The whole idea is to be watching the resources that matter for the controller.
// When a resource that the controller is interested in changes, the Watch triggers
// the controller’s reconciliation loop, ensuring that the actual state of the resource
// matches the desired state as defined in the controller’s logic.
//
// Notice how we configured the Manager to monitor events such as the creation, update,
// or deletion of a Custom Resource (CR) of the Memcached kind, as well as any changes
// to the Deployment that the controller manages and owns.
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
// Watch the Memcached CR(s) and trigger reconciliation whenever it
// is created, updated, or deleted
For(&cachev1alpha1.Memcached{}).
// Watch the Deployment managed by the MemcachedReconciler. If any changes occur to the Deployment
// owned and managed by this controller, it will trigger reconciliation, ensuring that the cluster
// state aligns with the desired state. See that the ownerRef was set when the Deployment was created.
Owns(&appsv1.Deployment{}).
Complete(r)
}
Loading

0 comments on commit 43690b1

Please sign in to comment.