diff --git a/.cursor/rules/krci-ai/go-dev.mdc b/.cursor/rules/krci-ai/go-dev.mdc new file mode 100644 index 00000000..75d10cbd --- /dev/null +++ b/.cursor/rules/krci-ai/go-dev.mdc @@ -0,0 +1,50 @@ +--- +description: +globs: [] +alwaysApply: false +--- + +# Go-dev Agent Activation + +CRITICAL: Carefully read the YAML agent definition below. Immediately activate the Go Developer persona by following the activation instructions, and remain in this persona until you receive an explicit command to exit. + +```yaml +agent: + identity: + name: "Go Developer" + id: go-developer-v1 + version: "1.0.0" + description: "Go developer for Go code implementation/debugging. Redirects requirements→PM/PO, architecture→architect, other languages→dev agents." + role: "Go Developer" + goal: "Implement clean, efficient Go code within Go dev scope" + icon: "💻" + + activation_prompt: + - Greet the user with your name and role, inform of available commands, then HALT to await instruction + - Offer to help with development tasks but wait for explicit user confirmation + - IMPORTANT!!! ALWAYS execute instructions from the customization field below + - Only execute tasks when user explicitly requests them + - "CRITICAL: When user selects a command, validate ONLY that command's required assets exist. If missing: HALT, report exact file, wait for user action." + - "NEVER validate unused commands or proceed with broken references" + - When loading any asset, use path resolution {project_root}/.krci-ai/{agents,tasks,data,templates}/*.md + + principles: + - "SCOPE: Go code implementation + Go code reviews. Redirect requirements→PM/PO, architecture→architect, other languages→dev." + - "Write clean, readable Go code following established patterns" + - "Test thoroughly with comprehensive coverage" + - "Document clearly for maintainability" + - "Handle errors gracefully and provide meaningful feedback" + + customization: "" + + commands: + help: "Show available commands" + chat: "(Default) Development consultation and code assistance" + implement-new-cr: "Implement Kubernetes Custom Resource" + review-code: "Review code for best practices" + exit: "Exit Go Developer persona and return to normal mode" + + tasks: + - ./.krci-ai/tasks/go-dev-implement-new-cr.md + - ./.krci-ai/tasks/go-dev-review-code.md +``` diff --git a/.krci-ai/agents/go-dev.yaml b/.krci-ai/agents/go-dev.yaml new file mode 100644 index 00000000..abe656be --- /dev/null +++ b/.krci-ai/agents/go-dev.yaml @@ -0,0 +1,38 @@ +agent: + identity: + name: "Go Developer" + id: go-developer-v1 + version: "1.0.0" + description: "Go developer for Go code implementation/debugging. Redirects requirements→PM/PO, architecture→architect, other languages→dev agents." + role: "Go Developer" + goal: "Implement clean, efficient Go code within Go dev scope" + icon: "💻" + + activation_prompt: + - Greet the user with your name and role, inform of available commands, then HALT to await instruction + - Offer to help with development tasks but wait for explicit user confirmation + - IMPORTANT!!! ALWAYS execute instructions from the customization field below + - Only execute tasks when user explicitly requests them + - "CRITICAL: When user selects a command, validate ONLY that command's required assets exist. If missing: HALT, report exact file, wait for user action." + - "NEVER validate unused commands or proceed with broken references" + - When loading any asset, use path resolution {project_root}/.krci-ai/{agents,tasks,data,templates}/*.md + + principles: + - "SCOPE: Go code implementation + Go code reviews. Redirect requirements→PM/PO, architecture→architect, other languages→dev." + - "Write clean, readable Go code following established patterns" + - "Test thoroughly with comprehensive coverage" + - "Document clearly for maintainability" + - "Handle errors gracefully and provide meaningful feedback" + + customization: "" + + commands: + help: "Show available commands" + chat: "(Default) Development consultation and code assistance" + implement-new-cr: "Implement Kubernetes Custom Resource" + review-code: "Review code for best practices" + exit: "Exit Go Developer persona and return to normal mode" + + tasks: + - ./.krci-ai/tasks/go-dev-implement-new-cr.md + - ./.krci-ai/tasks/go-dev-review-code.md diff --git a/.krci-ai/data/go-coding-standards.md b/.krci-ai/data/go-coding-standards.md new file mode 100644 index 00000000..286bab9c --- /dev/null +++ b/.krci-ai/data/go-coding-standards.md @@ -0,0 +1,287 @@ +# Go Development Instructions + +Follow idiomatic Go practices and community standards when writing Go code. These instructions are based on [Effective Go](https://go.dev/doc/effective_go), [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments), and [Google's Go Style Guide](https://google.github.io/styleguide/go/). + +## General Instructions + +- Write simple, clear, and idiomatic Go code +- Favor clarity and simplicity over cleverness +- Follow the principle of least surprise +- Keep the happy path left-aligned (minimize indentation) +- Return early to reduce nesting +- Make the zero value useful +- Document exported types, functions, methods, and packages +- Use Go modules for dependency management + +## Naming Conventions + +### Packages + +- Use lowercase, single-word package names +- Avoid underscores, hyphens, or mixedCaps +- Choose names that describe what the package provides, not what it contains +- Avoid generic names like `util`, `common`, or `base` +- Package names should be singular, not plural + +### Variables and Functions + +- Use mixedCaps or MixedCaps (camelCase) rather than underscores +- Keep names short but descriptive +- Use single-letter variables only for very short scopes (like loop indices) +- Exported names start with a capital letter +- Unexported names start with a lowercase letter +- Avoid stuttering (e.g., avoid `http.HTTPServer`, prefer `http.Server`) + +### Interfaces + +- Name interfaces with -er suffix when possible (e.g., `Reader`, `Writer`, `Formatter`) +- Single-method interfaces should be named after the method (e.g., `Read` → `Reader`) +- Keep interfaces small and focused + +### Constants + +- Use MixedCaps for exported constants +- Use mixedCaps for unexported constants +- Group related constants using `const` blocks +- Consider using typed constants for better type safety + +## Code Style and Formatting + +### Formatting + +- Always use `gofmt` to format code +- Use `goimports` to manage imports automatically +- Keep line length reasonable (no hard limit, but consider readability) +- Add blank lines to separate logical groups of code + +### Comments + +- Write comments in complete sentences +- Start sentences with the name of the thing being described +- Package comments should start with "Package [name]" +- Use line comments (`//`) for most comments +- Use block comments (`/* */`) sparingly, mainly for package documentation +- Document why, not what, unless the what is complex + +### Error Handling + +- Check errors immediately after the function call +- Don't ignore errors using `_` unless you have a good reason (document why) +- Wrap errors with context using `fmt.Errorf` with `%w` verb +- Create custom error types when you need to check for specific errors +- Place error returns as the last return value +- Name error variables `err` +- Keep error messages lowercase and don't end with punctuation + +## Architecture and Project Structure + +### Package Organization + +- Follow standard Go project layout conventions +- Keep `main` packages in `cmd/` directory +- Put reusable packages in `pkg/` or `internal/` +- Use `internal/` for packages that shouldn't be imported by external projects +- Group related functionality into packages +- Avoid circular dependencies + +### Dependency Management + +- Use Go modules (`go.mod` and `go.sum`) +- Keep dependencies minimal +- Regularly update dependencies for security patches +- Use `go mod tidy` to clean up unused dependencies +- Vendor dependencies only when necessary + +## Type Safety and Language Features + +### Type Definitions + +- Define types to add meaning and type safety +- Use struct tags for JSON, XML, database mappings +- Prefer explicit type conversions +- Use type assertions carefully and check the second return value + +### Pointers vs Values + +- Use pointers for large structs or when you need to modify the receiver +- Use values for small structs and when immutability is desired +- Be consistent within a type's method set +- Consider the zero value when choosing pointer vs value receivers + +### Interfaces and Composition + +- Accept interfaces, return concrete types +- Keep interfaces small (1-3 methods is ideal) +- Use embedding for composition +- Define interfaces close to where they're used, not where they're implemented +- Don't export interfaces unless necessary + +## Concurrency + +### Goroutines + +- Don't create goroutines in libraries; let the caller control concurrency +- Always know how a goroutine will exit +- Use `sync.WaitGroup` or channels to wait for goroutines +- Avoid goroutine leaks by ensuring cleanup + +### Channels + +- Use channels to communicate between goroutines +- Don't communicate by sharing memory; share memory by communicating +- Close channels from the sender side, not the receiver +- Use buffered channels when you know the capacity +- Use `select` for non-blocking operations + +### Synchronization + +- Use `sync.Mutex` for protecting shared state +- Keep critical sections small +- Use `sync.RWMutex` when you have many readers +- Prefer channels over mutexes when possible +- Use `sync.Once` for one-time initialization + +## Error Handling Patterns + +### Creating Errors + +- Use `errors.New` for simple static errors +- Use `fmt.Errorf` for dynamic errors +- Create custom error types for domain-specific errors +- Export error variables for sentinel errors +- Use `errors.Is` and `errors.As` for error checking + +### Error Propagation + +- Add context when propagating errors up the stack +- Don't log and return errors (choose one) +- Handle errors at the appropriate level +- Consider using structured errors for better debugging + +## API Design + +### HTTP Handlers + +- Use `http.HandlerFunc` for simple handlers +- Implement `http.Handler` for handlers that need state +- Use middleware for cross-cutting concerns +- Set appropriate status codes and headers +- Handle errors gracefully and return appropriate error responses + +### JSON APIs + +- Use struct tags to control JSON marshaling +- Validate input data +- Use pointers for optional fields +- Consider using `json.RawMessage` for delayed parsing +- Handle JSON errors appropriately + +## Performance Optimization + +### Memory Management + +- Minimize allocations in hot paths +- Reuse objects when possible (consider `sync.Pool`) +- Use value receivers for small structs +- Preallocate slices when size is known +- Avoid unnecessary string conversions + +### Profiling + +- Use built-in profiling tools (`pprof`) +- Benchmark critical code paths +- Profile before optimizing +- Focus on algorithmic improvements first +- Consider using `testing.B` for benchmarks + +## Testing + +### Test Organization + +- Keep tests in the same package (white-box testing) +- Use `_test` package suffix for black-box testing +- Name test files with `_test.go` suffix +- Place test files next to the code they test + +### Writing Tests + +- Use table-driven tests for multiple test cases +- Name tests descriptively using `Test_functionName_scenario` +- Use subtests with `t.Run` for better organization +- Test both success and error cases +- Use `testify` or similar libraries sparingly + +### Test Helpers + +- Mark helper functions with `t.Helper()` +- Create test fixtures for complex setup +- Use `testing.TB` interface for functions used in tests and benchmarks +- Clean up resources using `t.Cleanup()` + +## Security Best Practices + +### Input Validation + +- Validate all external input +- Use strong typing to prevent invalid states +- Sanitize data before using in SQL queries +- Be careful with file paths from user input +- Validate and escape data for different contexts (HTML, SQL, shell) + +### Cryptography + +- Use standard library crypto packages +- Don't implement your own cryptography +- Use crypto/rand for random number generation +- Store passwords using bcrypt or similar +- Use TLS for network communication + +## Documentation + +### Code Documentation + +- Document all exported symbols +- Start documentation with the symbol name +- Use examples in documentation when helpful +- Keep documentation close to code +- Update documentation when code changes + +### README and Documentation Files + +- Include clear setup instructions +- Document dependencies and requirements +- Provide usage examples +- Document configuration options +- Include troubleshooting section + +## Tools and Development Workflow + +### Essential Tools + +- `go fmt`: Format code +- `go vet`: Find suspicious constructs +- `golint` or `golangci-lint`: Additional linting +- `go test`: Run tests +- `go mod`: Manage dependencies +- `go generate`: Code generation + +### Development Practices + +- Run tests before committing +- Use pre-commit hooks for formatting and linting +- Keep commits focused and atomic +- Write meaningful commit messages +- Review diffs before committing + +## Common Pitfalls to Avoid + +- Not checking errors +- Ignoring race conditions +- Creating goroutine leaks +- Not using defer for cleanup +- Modifying maps concurrently +- Not understanding nil interfaces vs nil pointers +- Forgetting to close resources (files, connections) +- Using global variables unnecessarily +- Over-using empty interfaces (`interface{}`) +- Not considering the zero value of types diff --git a/.krci-ai/data/operator-best-practices.md b/.krci-ai/data/operator-best-practices.md new file mode 100644 index 00000000..70630e0a --- /dev/null +++ b/.krci-ai/data/operator-best-practices.md @@ -0,0 +1,95 @@ +# Operator Best Practices + +## Development + +Considerations for Operator developers: + +- An Operator should manage a single type of application, essentially following the UNIX principle: do one thing and do it well. + +- If an application consists of multiple tiers or components, multiple Operators should be written one for each of them. For example, if the application consists of Redis, AMQ, and MySQL, there should be 3 Operators, not one. + +- If there is significant orchestration and sequencing involved, an Operator should be written that represents the entire stack, in turn delegating to other Operators for orchestrating their part of it. + +- Operators should own a CRD and only one Operator should control a CRD on a cluster. Two Operators managing the same CRD is not a recommended best practice. An API that exists with multiple implementations is a typical example of a no-op Operator. The no-op Operator doesn't have any deployment or reconciliation loop to define the shared API and other Operators depend on this Operator to provide one implementation of the API, e.g. similar to PVCs or Ingress. + +- Inside an Operator, multiple controllers should be used if multiple CRDs are managed. This helps in separation of concerns and code readability. Note that this doesn't necessarily mean that we need to have one container image per controller, but rather one reconciliation loop (which could be running as part of the same Operator binary) per CRD. + +- An Operator shouldn't deploy or manage other operators (such patterns are known as meta or super operators or include CRDs in its Operands). It's the Operator Lifecycle Manager's job to manage the deployment and lifecycle of operators. For further information check [Dependency Resolution][Dependency Resolution]. + +- If multiple operators are packaged and shipped as a single entity by the same CSV for example, then it is recommended to add all owned and required CRDs, as well as all deployments for operators that manage the owned CRDs, to the same CSV. + +- Writing an Operator involves using the Kubernetes API, which in most scenarios will be built using same boilerplate code. Use a framework like the Operator SDK to save yourself time with this and to also get a suite of tooling to ease development and testing. + +- Operators shouldn’t make any assumptions about the namespace they are deployed in and they should not use hard-coded names of resources that they expect to already exist. + +- Operators shouldn’t hard code the namespaces they are watching. This should be configurable - having no namespace supplied is interpreted as watching all namespaces + +- Semantic versioning (aka semver) should be used to version an Operator. Operators are long-running workloads on the cluster and its APIs are potentially in need of support over a longer period of time. Use the [semver.org guidelines](https://semver.org) to help determine when and how to bump versions when there are breaking or non-breaking changes. + +- Kubernetes API versioning guidelines should be used to version Operator CRDs. Use the [Kubernetes sig-architecture guidelines](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md#so-you-want-to-change-the-api) to get best practices on when to bump versions and when breaking changes are acceptable. + +- When defining CRDs, you should use OpenAPI spec to create a structural schema for your CRDs. + +- Operators are instrumented to provide useful, actionable metrics to external systems (e.g. monitoring/alerting platforms). Minimally, metrics should represent the software's health and key performance indicators, as well as support the creation of [service levels indicators](https://en.wikipedia.org/wiki/Service_level_indicator) such as throughput, latency, availability, errors, capacity, etc. + +- Operators may create objects as part of their operational duty. Object accumulation can consume unnecessary resources, slow down the API and clutter the user interface. As such it is important for operators to keep good hygiene and to clean up resources when they are not needed. Here are instructions on [how to handle cleanup on deletion][advanced-topics]. + +### Summary + +- One Operator per managed application +- Multiple operators should be used for complex, multi-tier application stacks +- CRD can only be owned by a single Operator, shared CRDs should be owned by a separate Operator +- One controller per custom resource definition +- Use a tool like Operator SDK +- Do not hard-code namespaces or resources names +- Make watch namespace configurable +- Use semver / observe Kubernetes guidelines on versioning APIs +- Use OpenAPI spec with structural schema on CRDs +- Operators expose metrics to external systems +- Operators cleanup resources on deletion + +## Running On-Cluster + +Considerations for on-cluster behavior + +- Like all containers on Kubernetes, Operators need not run as root unless absolutely necessary. Operators should come with their own ServiceAccount and not rely on the `default`. + +- Operators should not self-register their CRDs. These are global resources and careful consideration needs to be taken when setting those up. Also this requires the Operator to have global privileges which is potentially dangerous compared to that little extra convenience. + +- Operators use CRs as the primary interface to the cluster user. As such, at all times, meaningful status information should be written to those objects unless they are solely used to store data in a structured schema. + +- Operators should be updated frequently according to server versioning. + +- Operators need to support updating managed applications (Operands) that were set up by an older version of the Operator. There are multiple models for this: + +| Model | Description | +| ------ | ----- | +| **Operator fan-out** | where the Operator allows the user to specify the version in the custom resource | +| **single version** | where the Operator is tied to the version of the operand. | +| **hybrid approach** | where the Operator is tied to a range of versions, and the user can select some level of the version. | + +- An Operator should not deploy another Operator - an additional component on cluster should take care of this (OLM). + +- When Operators change their APIs, CRD conversion (webhooks) should be used to deal with potentially older instances of them using the previous API version. + +- Operators should make it easy for users to use their APIs - validating and rejecting malformed requests via extensive Open API validation schema on CRDs or via an admission webhook is good practice. + +- The Operator itself should be really modest in its requirements - it should always be able to deploy by deploying its controllers, no user input should be required to start up the Operator. + +- If user input is required to change the configuration of the Operator itself, a Configuration CRD should be used. Init-containers as part of the Operator deployments can be used to create a default instance of those CRs and then the Operator manages their lifecycle. + +### Summary: + +On the cluster, an Operator... + +- Does not run as root +- Does not self-register CRDs +- Does not install other Operators +- Does rely on dependencies via package manager (OLM) +- Writes meaningful status information on Custom Resources objects unless pure data structure +- Should be capable of updating from a previous version of the Operator +- Should be capable of managing an Operand from an older Operator version +- Uses CRD conversion (webhooks) if API/CRDs change +- Uses OpenAPI validation / Admission Webhooks to reject invalid CRs +- Should always be able to deploy and come up without user input +- Offers (pre)configuration via a `“Configuration CR”` instantiated by InitContainers \ No newline at end of file diff --git a/.krci-ai/tasks/go-dev-implement-new-cr.md b/.krci-ai/tasks/go-dev-implement-new-cr.md new file mode 100644 index 00000000..855f732c --- /dev/null +++ b/.krci-ai/tasks/go-dev-implement-new-cr.md @@ -0,0 +1,304 @@ + +# Task: Implement a new Kubernetes Custom Resource + +## Description + +This guide provides a comprehensive prompt for LLM to implement a new Kubernetes Custom Resource. + +## Prerequisites + +**IMPORTANT**: Before starting implementation, you must read and fully understand the following documentation: + +1. [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) - Apply ALL the Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices defined in this document. + +## ⚠️ CRITICAL FIRST STEP + +**BEFORE ANY IMPLEMENTATION**: You MUST run the `make operator-sdk create api` command first to scaffold the proper structure. See Step 1.0 below for detailed instructions on how to do this. + +**DO NOT** manually create files before running this command! + +## Overview + +You are tasked with implementing a new Kubernetes Custom Resource for the `your-operator` project. This operator follows the chain of responsibility pattern for handling reconciliation logic. + +## Implementation Steps + +Follow the [Operator SDK Tutorial](https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/) as the foundation for implementing your controller. + +#### 1 Scaffold API and Controller + +**Before implementing the controller, ask the user for the CustomResource details:** + +1. **Group**: The API group (typically use `v1` for this project) +2. **Version**: The API version (typically `v1alpha1`) +3. **Kind**: The CustomResource kind name (e.g., `KeycloakClient`, `KeycloakUser`, etc.) + +Once you have these details, use the Operator SDK to scaffold the basic API and controller structure: + +```bash +make operator-sdk create api --group --version --kind --resource --controller +``` + +**Example**: If the user wants to create a `KeycloakClient` CustomResource: + +```bash +make operator-sdk create api --group v1 --version v1alpha1 --kind KeycloakClient --resource --controller +``` + +This command will create: + +- API types in `api/v1alpha1/` +- Controller skeleton in `internal/controller/` +- Basic RBAC markers + +After scaffolding, you'll need to customize the generated code to follow the project's specific patterns described in the sections below. + +#### 2 Implement the API Types + +Implement your Custom Resource Definition (CRD) spec and status, based on user requirements, in `api/v1alpha1/`: + +**Note**: The following examples use `YourResource` as a placeholder. Replace this with the actual resource name you specified during scaffolding. + +```go +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// YourResource is the Schema for the yourresources API +type YourResource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec YourResourceSpec `json:"spec,omitempty"` + Status YourResourceStatus `json:"status,omitempty"` +} + +// YourResourceSpec defines the desired state of YourResource +type YourResourceSpec struct { + // Add your spec fields here +} + +// YourResourceStatus defines the observed state of YourResource +type YourResourceStatus struct { + // Add your status fields here +} +``` + +#### 3 Generate Code and Manifests + +Run the following commands to generate the necessary code: + +```bash +make generate +make manifests +``` + +#### 4 Implement the Controller + +Implement your controller in `internal/controller/yourresource/` following the existing pattern: + +**Note**: Replace `YourResource` and `yourresource` with the actual resource name you specified during scaffolding. + +```go +package yourresource + +import ( + "context" + "fmt" + "time" + + "k8s.io/apimachinery/pkg/api/equality" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + 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/reconcile" + + yourresourceApi "github.com/your-org/your-operator/api/v1" // Replace with your actual module path +) + +const ( + defaultRequeueTime = time.Second * 30 + successRequeueTime = time.Minute * 10 + finalizerName = "yourresource.operator.finalizer.name" +) + +// NewReconcileYourResource creates a new ReconcileYourResource with all necessary dependencies. +func NewReconcileYourResource( + client client.Client, +) *ReconcileYourResource { + return &ReconcileYourResource{ + client: client, + } +} + +type ReconcileYourResource struct { + client client.Client +} + +func (r *ReconcileYourResource) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&yourresourceApi.YourResource{}). + Complete(r) +} + +// +kubebuilder:rbac:groups=yourgroup,namespace=placeholder,resources=yourresources,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=yourgroup,namespace=placeholder,resources=yourresources/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=yourgroup,namespace=placeholder,resources=yourresources/finalizers,verbs=update +// +kubebuilder:rbac:groups="",namespace=placeholder,resources=secrets,verbs=get;list;watch + +func (r *ReconcileYourResource) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + log := ctrl.LoggerFrom(ctx) + log.Info("Reconciling YourResource") + + yourResource := &yourresourceApi.YourResource{} + if err := r.client.Get(ctx, request.NamespacedName, yourResource); err != nil { + if k8sErrors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, err + } + + + if yourResource.GetDeletionTimestamp() != nil { + if controllerutil.ContainsFinalizer(yourResource, finalizerName) { + if err = chain.NewRemoveResource().ServeRequest(ctx, yourResource); err != nil { + return ctrl.Result{}, err + } + + controllerutil.RemoveFinalizer(yourResource, finalizerName) + + if err = r.client.Update(ctx, yourResource); err != nil { + return ctrl.Result{}, err + } + } + + return ctrl.Result{}, nil + } + + if controllerutil.AddFinalizer(yourResource, finalizerName) { + err = r.client.Update(ctx, yourResource) + if err != nil { + return ctrl.Result{}, err + } + + // Get yourResource again to get the updated object + if err = r.client.Get(ctx, request.NamespacedName, yourResource); err != nil { + return reconcile.Result{}, err + } + } + + oldStatus := yourResource.Status.DeepCopy() + + if err = chain.MakeChain(r.client).ServeRequest(ctx, yourResource); err != nil { + log.Error(err, "An error has occurred while handling YourResource") + + yourResource.Status.SetError(err.Error()) + + if statusErr := r.updateYourResourceStatus(ctx, yourResource, oldStatus); statusErr != nil { + return reconcile.Result{}, statusErr + } + + return reconcile.Result{}, err + } + + yourResource.Status.SetOK() + + if err = r.updateYourResourceStatus(ctx, yourResource, oldStatus); err != nil { + return reconcile.Result{}, err + } + + log.Info("Reconciling YourResource is finished") + + return reconcile.Result{ + RequeueAfter: successRequeueTime, + }, nil +} + +func (r *ReconcileYourResource) updateYourResourceStatus( + ctx context.Context, + yourResource *yourresourceApi.YourResource, + oldStatus yourresourceApi.YourResourceStatus, +) error { + if equality.Semantic.DeepEqual(&yourResource.Status, oldStatus) { + return nil + } + + if err := r.client.Status().Update(ctx, yourResource); err != nil { + return fmt.Errorf("failed to update YourResource status: %w", err) + } + + return nil +} +``` + +#### 5 Implement the Chain of Responsibility + +Create a chain package in `internal/controller/yourresource/chain/` with the following structure: + +1. `chain.go` - Main chain implementation +2. `factory.go` - Chain factory +3. Individual handler files for each step in the chain + +**Note**: Replace `yourresource` and `YourResource` with the actual resource name you specified during scaffolding. + +Example `chain.go`: + +```go +package chain + +import ( + "context" + "sigs.k8s.io/controller-runtime/pkg/client" + + yourApi "github.com/your-org/your-operator/api/v1" +) + +type Chain interface { + ServeRequest(ctx context.Context, yourResource *yourApi.YourResource) error +} + +type chain struct { + handlers []Handler +} + +func (c *chain) ServeRequest(ctx context.Context, yourResource *yourApi.YourResource) error { + for _, handler := range c.handlers { + if err := handler.ServeRequest(ctx, yourResource); err != nil { + return err + } + } + return nil +} + +type Handler interface { + ServeRequest(ctx context.Context, yourResource *yourApi.YourResource) error +} + +func MakeChain(k8sClient client.Client) Chain { + return &chain{ + handlers: []Handler{ + // Add your handlers here + }, + } +} +``` + +Example handler implementations should follow the pattern of existing handlers in your chain. + +#### 6 Register the Controller + +Add your controller to `cmd/main.go`: + +```go +import ( + yourresourcecontroller "github.com/your-org/your-operator/controllers/yourresource" +) + +// In the main function, add: +if err = yourresourcecontroller.NewReconcileYourResource(mgr.GetClient()).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "YourResource") + os.Exit(1) +} +``` + +**Note**: Replace `YourResource` with the actual resource name you specified during scaffolding. diff --git a/.krci-ai/tasks/go-dev-review-code.md b/.krci-ai/tasks/go-dev-review-code.md new file mode 100644 index 00000000..f7f0a516 --- /dev/null +++ b/.krci-ai/tasks/go-dev-review-code.md @@ -0,0 +1,54 @@ +# Task: Review Go code + +You are an expert Go developer and Kubernetes operator specialist tasked with reviewing Go code for quality, best practices, and adherence to standards. + +## Prerequisites + +**IMPORTANT**: Before starting your review, you must read and fully understand the following documentation: + +1. **Read** [Go Coding Standards](./.krci-ai/data/go-coding-standards.md) - Apply ALL the Go development standards, best practices, naming conventions, error handling patterns, testing guidelines, and security practices defined in this document. + +2. **Read** [Operator Best Practices](./.krci-ai/data/operator-best-practices.md) - Apply ALL the Kubernetes operator-specific patterns, architectural principles, CRD design guidelines, and operational practices defined in this document. + +Your review must be based on the standards and practices outlined in these documents. Do not proceed without reading them first. + +## Review Approach + +1. **Analyze the code** against all standards and practices from the required documentation +2. **Identify violations** of the established guidelines +3. **Provide specific, actionable feedback** with clear examples and references to the documentation + +## Review Output Format + +### Summary + +Brief overall assessment of code quality and adherence to standards. + +### Issues and Improvements + +For each issue found, provide: + +- **Category**: (e.g., "Go Standards Violation", "Operator Best Practice", "Security", etc.) +- **Severity**: Critical | High | Medium | Low +- **Description**: Clear explanation with reference to specific guideline from the documentation +- **Location**: File and line number references +- **Recommendation**: Specific fix with code example if helpful + +### Strengths + +Highlight what the code does well and follows best practices correctly. + +### Action Items + +Prioritized list of recommended fixes: + +1. Critical issues that must be addressed +2. Important improvements +3. Nice-to-have enhancements + +## Review Principles + +- Be constructive and educational +- Reference the specific guidelines from the documentation +- Provide concrete examples and suggestions +- Balance thoroughness with practicality