Skip to content

Commit

Permalink
Merge pull request #114 from Peefy/feat-health-check
Browse files Browse the repository at this point in the history
feat: add health check feature for flux kcl controller
  • Loading branch information
Peefy authored Aug 20, 2024
2 parents ed4a325 + 22c25dd commit 5d282dd
Show file tree
Hide file tree
Showing 12 changed files with 1,345 additions and 103 deletions.
17 changes: 4 additions & 13 deletions README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,10 @@ flux-kcl-controller 是一个组件,用于集成 [KCL](https://github.com/kcl-

## 创建测试用的 k8s 集群

使用`mycluster.yaml`创建集群:

```yaml
apiVersion: k3d.io/v1alpha2
kind: Simple
name: mycluster
servers: 1
agents: 2
```
通过如下命令创建集群:

```shell
k3d cluster create -c mycluster.yaml
k3d cluster create
```

## 下载 kcl-controller 并且安装到集群中
Expand All @@ -51,7 +42,7 @@ git clone https://github.com/kcl-lang/flux-kcl-controller.git
进入到本仓库的根目录:

```shell
cd kcl-controller
cd flux-kcl-controller
```

将 kcl-controller 安装到集群中:
Expand All @@ -62,7 +53,7 @@ make deploy

## 监控一个 git 仓库

我们以仓库 https://github.com/awesome-kusion/kcl-deployment 为例,该仓库中存储了一个 KCL 程序,该程序定义了一个 Deployment,我们将使用 kcl-controller 来部署该程序。
我们以仓库 https://github.com/awesome-kusion/kcl-deployment 为例,该仓库中存储了一个 KCL 程序,该程序定义了一个 Deployment,我们将使用 `flux-kcl-controller` 来部署该程序。

通过 `gitrepo.yaml` 文件,定义一个 `GitRepository` 对象,用来监控该仓库:

Expand Down Expand Up @@ -121,4 +112,4 @@ nginx-deployment 1/1 1 1 20m
nginx-deployment-1 1/1 1 0 4s
```

可以看到,kcl-controller 根据仓库中的 KCL 程序,创建了一个 nginx-deployment-1。
可以看到,`flux-kcl-controller` 根据仓库中的 KCL 程序,创建了一个 `nginx-deployment-1` 资源
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

# Introduction

The flux-kcl-controller is a component developed for the integration of [KCL](https://github.com/kcl-lang/kcl) and [Flux](https://github.com/fluxcd/flux2), designed to orchestrate continuous delivery pipelines for infrastructure and workloads defined with KCL based on the [source-controller](https://github.com/fluxcd/source-controller) to acquire the KCL program from repositories.
The `flux-kcl-controller` is a component developed for the integration of [KCL](https://github.com/kcl-lang/kcl) and [Flux](https://github.com/fluxcd/flux2), designed to orchestrate continuous delivery pipelines for infrastructure and workloads defined with KCL based on the [source-controller](https://github.com/fluxcd/source-controller), [kustomize-controller](https://github.com/fluxcd/kustomize-controller) and [helm-controller](https://github.com/fluxcd/helm-controller) to acquire the KCL program from repositories.

![kcl-flux](./docs/img/kcl-flux.png)

Expand Down Expand Up @@ -54,7 +54,7 @@ make deploy

## Monitor a git repository

Take the github repository https://github.com/awesome-kusion/kcl-deployment as an example. This repository stores a KCL program that defines a `Deployment`. We will use kcl-controller to deploy this program.
Take the GitHub repository https://github.com/awesome-kusion/kcl-deployment as an example. This repository stores a KCL program that defines a `Deployment`. We will use `flux-kcl-controller` to deploy this program.

Define a `GitRepository` object through the `gitrepo.yaml` file to monitor the repository:

Expand Down Expand Up @@ -111,8 +111,4 @@ nginx-deployment 1/1 1 1 20m
nginx-deployment-1 1/1 1 0 4s
```

kcl-controller creates a `nginx-deployment-1` according to the KCL program in the repository.

git tag v0.3.1
git push origin v0.3.1
gh release create v0.3.1 --draft --generate-notes --title "v0.3.1 Release"
`flux-kcl-controller` creates a `nginx-deployment-1` according to the KCL program in the repository.
34 changes: 34 additions & 0 deletions api/v1alpha1/inventory_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Copyright 2023 The Flux 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 v1alpha1

// ResourceInventory contains a list of Kubernetes resource object references
// that have been applied by a Kustomization.
type ResourceInventory struct {
// Entries of Kubernetes resource object references.
Entries []ResourceRef `json:"entries"`
}

// ResourceRef contains the information necessary to locate a resource within a cluster.
type ResourceRef struct {
// ID is the string representation of the Kubernetes resource object's metadata,
// in the format '<namespace>_<name>_<group>_<kind>'.
ID string `json:"id"`

// Version is the API version of the Kubernetes resource object's kind.
Version string `json:"v"`
}
138 changes: 133 additions & 5 deletions api/v1alpha1/kclrun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,30 @@ import (
)

const (
KCLRunKind = "KCLRun"
KCLRunKind = "KCLRun"
KCLRunFinalizer = "finalizers.fluxcd.io"
MaxConditionMessageLength = 20000
EnabledValue = "enabled"
DisabledValue = "disabled"
MergeValue = "Merge"
IfNotPresentValue = "IfNotPresent"
IgnoreValue = "Ignore"
)

// KCLRunSpec defines the desired state of KCLRun
type KCLRunSpec struct {
// CommonMetadata specifies the common labels and annotations that are
// applied to all resources. Any existing label or annotation will be
// overridden if its key matches a common one.
// +optional
CommonMetadata *CommonMetadata `json:"commonMetadata,omitempty"`

// DependsOn may contain a meta.NamespacedObjectReference slice
// with references to Kustomization resources that must be ready before this
// Kustomization can be reconciled.
// +optional
DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"`

// Timeout is the time to wait for any individual Kubernetes operation (like Jobs
// for hooks) during the performance. Defaults to '5m0s'.
// +kubebuilder:validation:Type=string
Expand Down Expand Up @@ -72,17 +91,122 @@ type KCLRunSpec struct {
// +optional
TargetNamespace string `json:"targetNamespace,omitempty"`

// Force instructs the controller to recreate resources
// when patching fails due to an immutable field change.
// +kubebuilder:default:=false
// +optional
Force bool `json:"force,omitempty"`

// The interval at which to reconcile the Kustomization.
// This interval is approximate and may be subject to jitter to ensure
// efficient use of resources.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`

// The interval at which to retry a previously failed reconciliation.
// When not specified, the controller uses the KustomizationSpec.Interval
// value to retry failures.
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +optional
RetryInterval *metav1.Duration `json:"retryInterval,omitempty"`

// Path to the directory containing the kcl.mod file.
// Defaults to 'None', which translates to the root path of the SourceRef.
// +optional
Path string `json:"path,omitempty"`

// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
// +optional
PostBuild *PostBuild `json:"postBuild,omitempty"`

// Prune enables garbage collection.
// +required
Prune bool `json:"prune"`

// A list of resources to be included in the health assessment.
// +optional
HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"`

// Wait instructs the controller to check the health of all the reconciled
// resources. When enabled, the HealthChecks are ignored. Defaults to false.
// +optional
Wait bool `json:"wait,omitempty"`

// Reference of the source where the kcl file is.
// +required
SourceRef kc.CrossNamespaceSourceReference `json:"sourceRef"`

// This flag tells the controller to suspend subsequent kustomize executions,
// it does not apply to already started executions. Defaults to false.
// +optional
Suspend bool `json:"suspend,omitempty"`
}

// CommonMetadata defines the common labels and annotations.
type CommonMetadata struct {
// Annotations to be added to the object's metadata.
// +optional
Annotations map[string]string `json:"annotations,omitempty"`

// Labels to be added to the object's metadata.
// +optional
Labels map[string]string `json:"labels,omitempty"`
}

// PostBuild describes which actions to perform on the YAML manifest
// generated by building the kustomize overlay.
type PostBuild struct {
// Substitute holds a map of key/value pairs.
// The variables defined in your YAML manifests that match any of the keys
// defined in the map will be substituted with the set value.
// Includes support for bash string replacement functions
// e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
// +optional
Substitute map[string]string `json:"substitute,omitempty"`

// SubstituteFrom holds references to ConfigMaps and Secrets containing
// the variables and their values to be substituted in the YAML manifests.
// The ConfigMap and the Secret data keys represent the var names, and they
// must match the vars declared in the manifests for the substitution to
// happen.
// +optional
SubstituteFrom []SubstituteReference `json:"substituteFrom,omitempty"`
}

// SubstituteReference contains a reference to a resource containing
// the variables name and value.
type SubstituteReference struct {
// Kind of the values referent, valid values are ('Secret', 'ConfigMap').
// +kubebuilder:validation:Enum=Secret;ConfigMap
// +required
Kind string `json:"kind"`

// Name of the values referent. Should reside in the same namespace as the
// referring resource.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +required
Name string `json:"name"`

// Optional indicates whether the referenced resource must exist, or whether to
// tolerate its absence. If true and the referenced resource is absent, proceed
// as if the resource was present but empty, without any variables defined.
// +kubebuilder:default:=false
// +optional
Optional bool `json:"optional,omitempty"`
}

// KCLRunStatus defines the observed state of KCLRun
type KCLRunStatus struct {
// The last successfully applied revision.
// Equals the Revision of the applied Artifact from the referenced Source.
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`

// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file

Expand Down Expand Up @@ -144,11 +268,15 @@ func (in KCLRun) GetReleaseNamespace() string {
}

// GetTimeout returns the configured Timeout, or the default of 300s.
func (in KCLRun) GetTimeout() metav1.Duration {
if in.Spec.Timeout == nil {
return metav1.Duration{Duration: 300 * time.Second}
func (in KCLRun) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration - 30*time.Second
if in.Spec.Timeout != nil {
duration = in.Spec.Timeout.Duration
}
if duration < 30*time.Second {
return 30 * time.Second
}
return *in.Spec.Timeout
return duration
}

// UsePersistentClient returns the configured PersistentClient, or the default
Expand Down
36 changes: 26 additions & 10 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/fluxcd/cli-utils/pkg/kstatus/polling"
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
"github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/runtime/events"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/runtime/metrics"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
Expand All @@ -42,6 +43,8 @@ import (
// +kubebuilder:scaffold:imports
)

const controllerName = "flux-kcl-controller"

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
Expand All @@ -61,6 +64,7 @@ func init() {
func main() {
var (
metricsAddr string
eventsAddr string
enableLeaderElection bool
httpRetry int
defaultServiceAccount string
Expand All @@ -69,17 +73,20 @@ func main() {
clientOptions client.Options
kubeConfigOpts client.KubeConfigOptions

rateLimiterOptions helper.RateLimiterOptions
watchOptions helper.WatchOptions
rateLimiterOptions helper.RateLimiterOptions
watchOptions helper.WatchOptions
disallowedFieldManagers []string
)

flag.StringVar(&metricsAddr, "metrics-addr", ":8083", "The address the metric endpoint binds to.")
flag.StringVar(&eventsAddr, "events-addr", "", "The address of the events receiver.")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.IntVar(&httpRetry, "http-retry", 9, "The maximum number of retries when failing to fetch artifacts over HTTP.")
flag.StringVar(&defaultServiceAccount, "default-service-account", "",
"Default service account used for impersonation.")
flag.StringArrayVar(&disallowedFieldManagers, "override-manager", []string{}, "Field manager disallowed to perform changes on managed resources.")

clientOptions.BindFlags(flag.CommandLine)
logOptions.BindFlags(flag.CommandLine)
Expand All @@ -103,20 +110,29 @@ func main() {
os.Exit(1)
}

var eventRecorder *events.Recorder
if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil {
setupLog.Error(err, "unable to create event recorder")
os.Exit(1)
}

jobStatusReader := statusreaders.NewCustomJobStatusReader(mgr.GetRESTMapper())
pollingOpts := polling.Options{
CustomStatusReaders: []engine.StatusReader{jobStatusReader},
}

if err = (&controller.KCLRunReconciler{
DefaultServiceAccount: defaultServiceAccount,
Client: mgr.GetClient(),
Metrics: helper.NewMetrics(mgr, metrics.MustMakeRecorder(), "finalizers.krm.kcl.dev.fluxcd"),
GetClusterConfig: ctrl.GetConfig,
ClientOpts: clientOptions,
KubeConfigOpts: kubeConfigOpts,
PollingOpts: pollingOpts,
StatusPoller: polling.NewStatusPoller(mgr.GetClient(), mgr.GetRESTMapper(), pollingOpts),
ControllerName: controllerName,
DefaultServiceAccount: defaultServiceAccount,
Client: mgr.GetClient(),
Metrics: helper.NewMetrics(mgr, metrics.MustMakeRecorder(), "finalizers.krm.kcl.dev.fluxcd"),
EventRecorder: eventRecorder,
GetClusterConfig: ctrl.GetConfig,
ClientOpts: clientOptions,
KubeConfigOpts: kubeConfigOpts,
PollingOpts: pollingOpts,
StatusPoller: polling.NewStatusPoller(mgr.GetClient(), mgr.GetRESTMapper(), pollingOpts),
DisallowedFieldManagers: disallowedFieldManagers,
}).SetupWithManager(mgr, controller.KCLRunReconcilerOptions{
HTTPRetry: httpRetry,
}); err != nil {
Expand Down
Loading

0 comments on commit 5d282dd

Please sign in to comment.