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 Aug 24, 2024
1 parent eb0f00b commit b72f4da
Show file tree
Hide file tree
Showing 7 changed files with 520 additions and 517 deletions.
535 changes: 196 additions & 339 deletions docs/book/src/getting-started.md

Large diffs are not rendered by default.

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
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 @@ -7,5 +7,3 @@ spec:
# 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,8 +19,7 @@ package controller
import (
"context"
"fmt"
"os"
"strings"

"time"

appsv1 "k8s.io/api/apps/v1"
Expand All @@ -33,14 +32,12 @@ import (
"k8s.io/client-go/tools/record"
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 Down Expand Up @@ -117,79 +114,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 +206,12 @@ 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))
}

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

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

dep := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -321,42 +220,8 @@ func (r *MemcachedReconciler) deploymentForMemcached(
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
},
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
Expand All @@ -383,7 +248,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 @@ -401,31 +266,6 @@ 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(name string) 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.
// Note that the Deployment will be also watched in order to ensure its
// desirable state on the cluster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ var _ = Describe("Memcached controller", func() {
Namespace: namespace.Name,
},
Spec: cachev1alpha1.MemcachedSpec{
Size: 1,
ContainerPort: 11211,
Size: 1,
},
}

Expand Down
Loading

0 comments on commit b72f4da

Please sign in to comment.