commit
53e52ba39a
|
@ -24,52 +24,56 @@ linters-settings:
|
|||
rules:
|
||||
- name: dot-imports
|
||||
disabled: true
|
||||
mnd:
|
||||
checks:
|
||||
- argument
|
||||
- case
|
||||
- operation
|
||||
- return
|
||||
- assign
|
||||
ignored-functions:
|
||||
- '^len\.'
|
||||
- '^strings\.SplitN$'
|
||||
- '^make$'
|
||||
wsl:
|
||||
allow-cuddle-declarations: true
|
||||
allow-separated-leading-comment: true
|
||||
nlreturn:
|
||||
block-size: 2
|
||||
testpackage:
|
||||
skip-regexp: test/e2e/...
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: test
|
||||
linters:
|
||||
- wrapcheck
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- cyclop
|
||||
- dupl
|
||||
- exhaustive
|
||||
# abandoned
|
||||
- deadcode
|
||||
- golint
|
||||
- ifshort
|
||||
- varcheck
|
||||
- structcheck
|
||||
- scopelint
|
||||
- nosnakecase
|
||||
- maligned
|
||||
- exhaustivestruct
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- funlen
|
||||
- interfacer
|
||||
# keep
|
||||
- varnamelen
|
||||
- godox # there are a bunch of TODOs that must be solved
|
||||
- gci
|
||||
- gofumpt
|
||||
- exhaustruct
|
||||
- paralleltest
|
||||
- ireturn
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocyclo
|
||||
- godox
|
||||
- goerr113
|
||||
- gofumpt
|
||||
- golint
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- interfacer
|
||||
- ireturn
|
||||
- lll
|
||||
- maligned
|
||||
- nakedret
|
||||
- nestif
|
||||
- nilnil
|
||||
- nlreturn
|
||||
- nosnakecase
|
||||
- paralleltest
|
||||
- rowserrcheck
|
||||
- scopelint
|
||||
- structcheck
|
||||
- sqlclosecheck
|
||||
- tagliatelle
|
||||
- tenv
|
||||
- testpackage
|
||||
- varnamelen
|
||||
- wastedassign
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
- varcheck
|
||||
- deadcode
|
||||
- ifshort
|
||||
- inamedparam
|
||||
- depguard
|
||||
# validate
|
||||
- cyclop
|
||||
- gocognit
|
||||
|
|
|
@ -18,8 +18,11 @@ package v1alpha1
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ErrUnmarshalOnNil = errors.New("UnmarshalJSON on nil pointer")
|
||||
|
||||
// RawMessage is a raw encoded JSON value.
|
||||
// It implements Marshaler and Unmarshaler and can
|
||||
// be used to delay JSON decoding or precompute a JSON encoding.
|
||||
|
@ -40,15 +43,18 @@ func (m RawMessage) MarshalJSON() ([]byte, error) {
|
|||
if m == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data.
|
||||
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||
if m == nil {
|
||||
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
|
||||
return fmt.Errorf("json.RawMessage: %w", ErrUnmarshalOnNil)
|
||||
}
|
||||
|
||||
*m = append((*m)[0:0], data...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -57,10 +63,12 @@ func (m *RawMessage) String() string {
|
|||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
b, err := m.MarshalJSON()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,7 @@ func init() {
|
|||
}
|
||||
|
||||
func NewRunCmd() *cobra.Command {
|
||||
|
||||
controllerOpts := controller.Options{
|
||||
co := controller.Options{
|
||||
MetricsAddr: ":8080",
|
||||
ProbeAddr: ":8081",
|
||||
PprofAddr: "",
|
||||
|
@ -53,7 +52,7 @@ func NewRunCmd() *cobra.Command {
|
|||
return fmt.Errorf("unable to compute cache's watch selector: %w", err)
|
||||
}
|
||||
|
||||
controllerOpts.WatchSelectors = map[rtclient.Object]rtcache.ByObject{
|
||||
co.WatchSelectors = map[rtclient.Object]rtcache.ByObject{
|
||||
// k8s
|
||||
&rbacv1.ClusterRole{}: {Label: selector},
|
||||
&rbacv1.ClusterRoleBinding{}: {Label: selector},
|
||||
|
@ -67,7 +66,7 @@ func NewRunCmd() *cobra.Command {
|
|||
&appsv1.Deployment{}: {Label: selector},
|
||||
}
|
||||
|
||||
return controller.Start(controllerOpts, func(manager manager.Manager, opts controller.Options) error {
|
||||
return controller.Start(co, func(manager manager.Manager, opts controller.Options) error {
|
||||
if _, err := controlplane.NewReconciler(cmd.Context(), manager, helmOpts); err != nil {
|
||||
return fmt.Errorf("unable to set-up DaprControlPlane reconciler: %w", err)
|
||||
}
|
||||
|
@ -81,16 +80,24 @@ func NewRunCmd() *cobra.Command {
|
|||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&controllerOpts.LeaderElectionID, "leader-election-id", controllerOpts.LeaderElectionID, "The leader election ID of the operator.")
|
||||
cmd.Flags().StringVar(&controllerOpts.LeaderElectionNamespace, "leader-election-namespace", controllerOpts.LeaderElectionNamespace, "The leader election namespace.")
|
||||
cmd.Flags().BoolVar(&controllerOpts.EnableLeaderElection, "leader-election", controllerOpts.EnableLeaderElection, "Enable leader election for controller manager.")
|
||||
cmd.Flags().BoolVar(&controllerOpts.ReleaseLeaderElectionOnCancel, "leader-election-release", controllerOpts.ReleaseLeaderElectionOnCancel, "If the leader should step down voluntarily.")
|
||||
cmd.Flags().StringVar(
|
||||
&co.LeaderElectionID, "leader-election-id", co.LeaderElectionID, "The leader election ID of the operator.")
|
||||
cmd.Flags().StringVar(
|
||||
&co.LeaderElectionNamespace, "leader-election-namespace", co.LeaderElectionNamespace, "The leader election namespace.")
|
||||
cmd.Flags().BoolVar(
|
||||
&co.EnableLeaderElection, "leader-election", co.EnableLeaderElection, "Enable leader election for controller manager.")
|
||||
cmd.Flags().BoolVar(
|
||||
&co.ReleaseLeaderElectionOnCancel, "leader-election-release", co.ReleaseLeaderElectionOnCancel, "If the leader should step down voluntarily.")
|
||||
|
||||
cmd.Flags().StringVar(&controllerOpts.MetricsAddr, "metrics-bind-address", controllerOpts.MetricsAddr, "The address the metric endpoint binds to.")
|
||||
cmd.Flags().StringVar(&controllerOpts.ProbeAddr, "health-probe-bind-address", controllerOpts.ProbeAddr, "The address the probe endpoint binds to.")
|
||||
cmd.Flags().StringVar(&controllerOpts.PprofAddr, "pprof-bind-address", controllerOpts.PprofAddr, "The address the pprof endpoint binds to.")
|
||||
cmd.Flags().StringVar(
|
||||
&co.MetricsAddr, "metrics-bind-address", co.MetricsAddr, "The address the metric endpoint binds to.")
|
||||
cmd.Flags().StringVar(
|
||||
&co.ProbeAddr, "health-probe-bind-address", co.ProbeAddr, "The address the probe endpoint binds to.")
|
||||
cmd.Flags().StringVar(
|
||||
&co.PprofAddr, "pprof-bind-address", co.PprofAddr, "The address the pprof endpoint binds to.")
|
||||
|
||||
cmd.Flags().StringVar(&helmOpts.ChartsDir, "helm-charts-dir", helmOpts.ChartsDir, "Helm charts dir.")
|
||||
cmd.Flags().StringVar(
|
||||
&helmOpts.ChartsDir, "helm-charts-dir", helmOpts.ChartsDir, "Helm charts dir.")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ package controlplane
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/openshift"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/helm"
|
||||
|
||||
|
@ -41,6 +44,7 @@ import (
|
|||
func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options) (*Reconciler, error) {
|
||||
c, err := client.NewClient(manager.GetConfig(), manager.GetScheme(), manager.GetClient())
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -52,10 +56,12 @@ func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options)
|
|||
rec.manager = manager
|
||||
rec.recorder = manager.GetEventRecorderFor(controller.FieldManager)
|
||||
|
||||
isOpenshift, err := c.IsOpenShift()
|
||||
isOpenshift, err := openshift.IsOpenShift(c.Discovery)
|
||||
if err != nil {
|
||||
//nolint: wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isOpenshift {
|
||||
rec.ClusterType = controller.ClusterTypeOpenShift
|
||||
}
|
||||
|
@ -65,7 +71,7 @@ func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options)
|
|||
|
||||
hc, err := loader.Load(o.ChartsDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to load chart from dir %s: %w", o.ChartsDir, err)
|
||||
}
|
||||
|
||||
rec.c = hc
|
||||
|
@ -111,6 +117,7 @@ func (r *Reconciler) init(ctx context.Context) error {
|
|||
for i := range r.actions {
|
||||
b, err := r.actions[i].Configure(ctx, r.Client, c)
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -119,7 +126,7 @@ func (r *Reconciler) init(ctx context.Context) error {
|
|||
|
||||
ct, err := c.Build(r)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failure building the application controller for DaprControlPlane resource: %w", err)
|
||||
}
|
||||
|
||||
r.controller = ct
|
||||
|
|
|
@ -2,6 +2,7 @@ package controlplane
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/controller"
|
||||
|
||||
|
@ -61,7 +62,11 @@ func (a *ApplyAction) Run(ctx context.Context, rr *ReconciliationRequest) error
|
|||
FieldManager: controller.FieldManager,
|
||||
})
|
||||
|
||||
return err
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure to apply changes to %s: %w", rr.NamespacedName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ApplyAction) Cleanup(_ context.Context, _ *ReconciliationRequest) error {
|
||||
|
|
|
@ -2,6 +2,7 @@ package controlplane
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/internal/controller/operator/instance"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
@ -44,7 +45,7 @@ func (a *StatusAction) Run(ctx context.Context, rr *ReconciliationRequest) error
|
|||
)
|
||||
|
||||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return err
|
||||
return fmt.Errorf("failure to lookup resource %s: %w", rr.NamespacedName, err)
|
||||
}
|
||||
|
||||
for i := range di.Status.Conditions {
|
||||
|
|
|
@ -89,32 +89,30 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("error updating DaprControlPlane resource: %w", err)
|
||||
}
|
||||
|
||||
//nolint:nestif
|
||||
if rr.Resource.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
|
||||
//
|
||||
// Add finalizer
|
||||
//
|
||||
|
||||
if ctrlutil.AddFinalizer(rr.Resource, DaprControlPlaneFinalizerName) {
|
||||
if err := r.Update(ctx, rr.Resource); err != nil {
|
||||
if k8serrors.IsConflict(err) {
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("conflict when adding finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, fmt.Errorf("failure adding finalizer to connector cluster %s: %w", req.NamespacedName, err)
|
||||
return ctrl.Result{}, fmt.Errorf("failure adding finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
//
|
||||
// Cleanup leftovers if needed
|
||||
//
|
||||
|
||||
for i := len(r.actions) - 1; i >= 0; i-- {
|
||||
if err := r.actions[i].Cleanup(ctx, &rr); err != nil {
|
||||
//nolint:wrapcheck
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
@ -122,11 +120,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
//
|
||||
// Handle finalizer
|
||||
//
|
||||
|
||||
if ctrlutil.RemoveFinalizer(rr.Resource, DaprControlPlaneFinalizerName) {
|
||||
if err := r.Update(ctx, rr.Resource); err != nil {
|
||||
if k8serrors.IsConflict(err) {
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("conflict when removing finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, fmt.Errorf("failure removing finalizer from %s: %w", req.NamespacedName, err)
|
||||
|
@ -149,6 +146,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
}
|
||||
|
||||
errs := make([]error, 0, len(r.actions)+1)
|
||||
|
||||
for i := range r.actions {
|
||||
if err := r.actions[i].Run(ctx, &rr); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
|
|
@ -25,7 +25,7 @@ type ReconciliationRequest struct {
|
|||
}
|
||||
|
||||
type Action interface {
|
||||
Configure(context.Context, *client.Client, *builder.Builder) (*builder.Builder, error)
|
||||
Run(context.Context, *ReconciliationRequest) error
|
||||
Cleanup(context.Context, *ReconciliationRequest) error
|
||||
Configure(ctx context.Context, c *client.Client, b *builder.Builder) (*builder.Builder, error)
|
||||
Run(ctx context.Context, rc *ReconciliationRequest) error
|
||||
Cleanup(ctx context.Context, rc *ReconciliationRequest) error
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ package instance
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/openshift"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/helm"
|
||||
|
||||
|
@ -47,6 +50,7 @@ import (
|
|||
func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options) (*Reconciler, error) {
|
||||
c, err := client.NewClient(manager.GetConfig(), manager.GetScheme(), manager.GetClient())
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -58,10 +62,12 @@ func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options)
|
|||
rec.manager = manager
|
||||
rec.recorder = manager.GetEventRecorderFor(controller.FieldManager)
|
||||
|
||||
isOpenshift, err := c.IsOpenShift()
|
||||
isOpenshift, err := openshift.IsOpenShift(c.Discovery)
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isOpenshift {
|
||||
rec.ClusterType = controller.ClusterTypeOpenShift
|
||||
}
|
||||
|
@ -73,7 +79,7 @@ func NewReconciler(ctx context.Context, manager ctrlRt.Manager, o helm.Options)
|
|||
|
||||
hc, err := loader.Load(o.ChartsDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to load chart from dir %s: %w", o.ChartsDir, err)
|
||||
}
|
||||
|
||||
rec.c = hc
|
||||
|
@ -143,6 +149,7 @@ func (r *Reconciler) init(ctx context.Context) error {
|
|||
for i := range r.actions {
|
||||
b, err := r.actions[i].Configure(ctx, r.Client, c)
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -151,7 +158,7 @@ func (r *Reconciler) init(ctx context.Context) error {
|
|||
|
||||
ct, err := c.Build(r)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failure building the application controller for DaprInstance resource: %w", err)
|
||||
}
|
||||
|
||||
r.controller = ct
|
||||
|
@ -160,7 +167,7 @@ func (r *Reconciler) init(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (r *Reconciler) Watch(obj ctrlCli.Object, eh handler.EventHandler, predicates ...predicate.Predicate) error {
|
||||
return r.controller.Watch(
|
||||
err := r.controller.Watch(
|
||||
source.Kind(
|
||||
r.manager.GetCache(),
|
||||
obj,
|
||||
|
@ -168,6 +175,16 @@ func (r *Reconciler) Watch(obj ctrlCli.Object, eh handler.EventHandler, predicat
|
|||
predicates...,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error configuring watcher for resource %s:%s, reson: %w",
|
||||
obj.GetObjectKind().GroupVersionKind().String(),
|
||||
obj.GetName(),
|
||||
err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) EnqueueRequestForOwner(owner ctrlCli.Object, opts ...handler.OwnerOption) handler.EventHandler {
|
||||
|
|
|
@ -72,11 +72,12 @@ func (a *ApplyCRDsAction) Run(ctx context.Context, rc *ReconciliationRequest) er
|
|||
})
|
||||
|
||||
apply := rc.Resource.Generation != rc.Resource.Status.ObservedGeneration
|
||||
_, err = dc.Get(ctx, obj.GetName(), metav1.GetOptions{})
|
||||
|
||||
_, err = dc.Get(ctx, obj.GetName(), metav1.GetOptions{})
|
||||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return fmt.Errorf("cannot determine if CRD %s exists: %w", resources.Ref(&obj), err)
|
||||
}
|
||||
|
||||
if err != nil && k8serrors.IsNotFound(err) {
|
||||
apply = true
|
||||
}
|
||||
|
@ -105,7 +106,6 @@ func (a *ApplyCRDsAction) Run(ctx context.Context, rc *ReconciliationRequest) er
|
|||
"apply", "true",
|
||||
"gen", rc.Resource.Generation,
|
||||
"ref", resources.Ref(&obj))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,6 @@ func (a *ApplyResourcesAction) Run(ctx context.Context, rc *ReconciliationReques
|
|||
})
|
||||
|
||||
switch dc.(type) {
|
||||
|
||||
//
|
||||
// NamespacedResource: in this case, filtering with ownership can be implemented
|
||||
// as all the namespaced resources created by this controller have the Dapr CR as
|
||||
|
@ -120,7 +119,6 @@ func (a *ApplyResourcesAction) Run(ctx context.Context, rc *ReconciliationReques
|
|||
r := gvk.GroupVersion().String() + ":" + gvk.Kind
|
||||
|
||||
if _, ok := a.subscriptions[r]; !ok {
|
||||
|
||||
a.l.Info("watch", "ref", r)
|
||||
|
||||
err = rc.Reconciler.Watch(
|
||||
|
@ -152,7 +150,6 @@ func (a *ApplyResourcesAction) Run(ctx context.Context, rc *ReconciliationReques
|
|||
r := gvk.GroupVersion().String() + ":" + gvk.Kind
|
||||
|
||||
if _, ok := a.subscriptions[r]; !ok {
|
||||
|
||||
a.l.Info("watch", "ref", r)
|
||||
|
||||
err = rc.Reconciler.Watch(
|
||||
|
@ -196,7 +193,6 @@ func (a *ApplyResourcesAction) Run(ctx context.Context, rc *ReconciliationReques
|
|||
// - https://github.com/dapr/dapr/issues/3968
|
||||
// - https://github.com/dapr/dapr/issues/6500
|
||||
//
|
||||
|
||||
a.l.Info("run",
|
||||
"apply", "false",
|
||||
"ref", resources.Ref(&obj),
|
||||
|
@ -284,9 +280,11 @@ func (a *ApplyResourcesAction) watchForUpdates(gvk schema.GroupVersionKind) bool
|
|||
if gvk.Group == "" && gvk.Version == "v1" && gvk.Kind == "Secret" {
|
||||
return false
|
||||
}
|
||||
|
||||
if gvk.Group == "admissionregistration.k8s.io" && gvk.Version == "v1" && gvk.Kind == "MutatingWebhookConfiguration" {
|
||||
return false
|
||||
}
|
||||
|
||||
if gvk.Group == "apiextensions.k8s.io" && gvk.Version == "v1" && gvk.Kind == "CustomResourceDefinition" {
|
||||
return false
|
||||
}
|
||||
|
@ -306,9 +304,11 @@ func (a *ApplyResourcesAction) installOnly(gvk schema.GroupVersionKind) bool {
|
|||
if gvk.Group == "" && gvk.Version == "v1" && gvk.Kind == "Secret" {
|
||||
return true
|
||||
}
|
||||
|
||||
if gvk.Group == "admissionregistration.k8s.io" && gvk.Version == "v1" && gvk.Kind == "MutatingWebhookConfiguration" {
|
||||
return true
|
||||
}
|
||||
|
||||
if gvk.Group == "apiextensions.k8s.io" && gvk.Version == "v1" && gvk.Kind == "CustomResourceDefinition" {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/api/operator/v1alpha1"
|
||||
|
@ -40,33 +42,11 @@ func (a *ChartAction) Run(ctx context.Context, rc *ReconciliationRequest) error
|
|||
rc.Resource.Status.Chart = &v1alpha1.ChartMeta{}
|
||||
}
|
||||
|
||||
rc.Resource.Status.Chart.Repo = ChartRepoEmbedded
|
||||
|
||||
// TODO: maybe cache the chart
|
||||
if rc.Resource.Spec.Chart != nil {
|
||||
opts := helm.ChartOptions{}
|
||||
opts.Name = rc.Resource.Spec.Chart.Name
|
||||
opts.RepoURL = rc.Resource.Spec.Chart.Repo
|
||||
opts.Version = rc.Resource.Spec.Chart.Version
|
||||
|
||||
if rc.Resource.Spec.Chart.Secret != "" {
|
||||
s, err := rc.Client.CoreV1().Secrets(rc.Resource.Namespace).Get(
|
||||
ctx,
|
||||
rc.Resource.Spec.Chart.Secret,
|
||||
metav1.GetOptions{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch secret %s, %w", rc.Resource.Spec.Chart.Secret, err)
|
||||
}
|
||||
|
||||
if v, ok := s.Data[ChartRepoUsernameKey]; ok {
|
||||
opts.Username = string(v)
|
||||
}
|
||||
if v, ok := s.Data[ChartRepoPasswordKey]; ok {
|
||||
opts.Password = string(v)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := a.engine.Load(opts)
|
||||
c, err := a.loadChart(ctx, rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -74,8 +54,6 @@ func (a *ChartAction) Run(ctx context.Context, rc *ReconciliationRequest) error
|
|||
rc.Chart = c
|
||||
|
||||
rc.Resource.Status.Chart.Repo = rc.Resource.Spec.Chart.Repo
|
||||
} else {
|
||||
rc.Resource.Status.Chart.Repo = ChartRepoEmbedded
|
||||
}
|
||||
|
||||
rc.Resource.Status.Chart.Version = rc.Chart.Metadata.Version
|
||||
|
@ -87,3 +65,38 @@ func (a *ChartAction) Run(ctx context.Context, rc *ReconciliationRequest) error
|
|||
func (a *ChartAction) Cleanup(_ context.Context, _ *ReconciliationRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ChartAction) loadChart(ctx context.Context, rc *ReconciliationRequest) (*chart.Chart, error) {
|
||||
opts := helm.ChartOptions{}
|
||||
opts.Name = rc.Resource.Spec.Chart.Name
|
||||
opts.RepoURL = rc.Resource.Spec.Chart.Repo
|
||||
opts.Version = rc.Resource.Spec.Chart.Version
|
||||
|
||||
if rc.Resource.Spec.Chart.Secret != "" {
|
||||
s, err := rc.Client.CoreV1().Secrets(rc.Resource.Namespace).Get(
|
||||
ctx,
|
||||
rc.Resource.Spec.Chart.Secret,
|
||||
metav1.GetOptions{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to fetch secret %s, %w", rc.Resource.Spec.Chart.Secret, err)
|
||||
}
|
||||
|
||||
if v, ok := s.Data[ChartRepoUsernameKey]; ok {
|
||||
opts.Username = string(v)
|
||||
}
|
||||
|
||||
if v, ok := s.Data[ChartRepoPasswordKey]; ok {
|
||||
opts.Password = string(v)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := a.engine.Load(opts)
|
||||
if err != nil {
|
||||
//nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ func (a *ConditionsAction) Run(ctx context.Context, rc *ReconciliationRequest) e
|
|||
}
|
||||
|
||||
ready := 0
|
||||
|
||||
for i := range deployments.Items {
|
||||
if conditions.ConditionStatus(deployments.Items[i], appsv1.DeploymentAvailable) == corev1.ConditionTrue {
|
||||
ready++
|
||||
|
|
|
@ -96,32 +96,30 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("error updating DaprInstance resource: %w", err)
|
||||
}
|
||||
|
||||
//nolint:nestif
|
||||
if rr.Resource.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
|
||||
//
|
||||
// Add finalizer
|
||||
//
|
||||
|
||||
if ctrlutil.AddFinalizer(rr.Resource, DaprInstanceFinalizerName) {
|
||||
if err := r.Update(ctx, rr.Resource); err != nil {
|
||||
if k8serrors.IsConflict(err) {
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("conflict when adding finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, fmt.Errorf("failure adding finalizer to connector cluster %s: %w", req.NamespacedName, err)
|
||||
return ctrl.Result{}, fmt.Errorf("failure adding finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
//
|
||||
// Cleanup leftovers if needed
|
||||
//
|
||||
|
||||
for i := len(r.actions) - 1; i >= 0; i-- {
|
||||
if err := r.actions[i].Cleanup(ctx, &rr); err != nil {
|
||||
//nolint:wrapcheck
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
@ -129,11 +127,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
//
|
||||
// Handle finalizer
|
||||
//
|
||||
|
||||
if ctrlutil.RemoveFinalizer(rr.Resource, DaprInstanceFinalizerName) {
|
||||
if err := r.Update(ctx, rr.Resource); err != nil {
|
||||
if k8serrors.IsConflict(err) {
|
||||
return ctrl.Result{}, err
|
||||
return ctrl.Result{}, fmt.Errorf("conflict when removing finalizer to %s: %w", req.NamespacedName, err)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, fmt.Errorf("failure removing finalizer from %s: %w", req.NamespacedName, err)
|
||||
|
@ -156,6 +153,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
}
|
||||
|
||||
errs := make([]error, 0, len(r.actions)+1)
|
||||
|
||||
for i := range r.actions {
|
||||
if err := r.actions[i].Run(ctx, &rr); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
@ -184,6 +182,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
//
|
||||
|
||||
err = r.Status().Update(ctx, rr.Resource)
|
||||
|
||||
if err != nil && k8serrors.IsConflict(err) {
|
||||
l.Info(err.Error())
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
)
|
||||
|
||||
func gcSelector(rc *ReconciliationRequest) (labels.Selector, error) {
|
||||
|
||||
namespace, err := labels.NewRequirement(
|
||||
helm.ReleaseNamespace,
|
||||
selection.Equals,
|
||||
|
@ -59,10 +58,12 @@ func labelsToRequest(_ context.Context, object ctrlCli.Object) []reconcile.Reque
|
|||
if allLabels == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
name := allLabels[helm.ReleaseName]
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
namespace := allLabels[helm.ReleaseNamespace]
|
||||
if namespace == "" {
|
||||
return nil
|
||||
|
|
|
@ -28,7 +28,7 @@ type ReconciliationRequest struct {
|
|||
}
|
||||
|
||||
type Action interface {
|
||||
Configure(context.Context, *client.Client, *builder.Builder) (*builder.Builder, error)
|
||||
Run(context.Context, *ReconciliationRequest) error
|
||||
Cleanup(context.Context, *ReconciliationRequest) error
|
||||
Configure(ctx context.Context, c *client.Client, b *builder.Builder) (*builder.Builder, error)
|
||||
Run(ctx context.Context, rc *ReconciliationRequest) error
|
||||
Cleanup(ctx context.Context, rc *ReconciliationRequest) error
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ func Get(from Getter, t ConditionType) *metav1.Condition {
|
|||
return &condition
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/openshift"
|
||||
"golang.org/x/time/rate"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
|
@ -23,6 +23,10 @@ import (
|
|||
daprClient "github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
const (
|
||||
DiscoveryLimiterBurst = 30
|
||||
)
|
||||
|
||||
var scaleConverter = scale.NewScaleConverter()
|
||||
var codecs = serializer.NewCodecFactory(scaleConverter.Scheme())
|
||||
|
||||
|
@ -44,30 +48,34 @@ type Client struct {
|
|||
}
|
||||
|
||||
func NewClient(cfg *rest.Config, scheme *runtime.Scheme, cc ctrl.Client) (*Client, error) {
|
||||
|
||||
discoveryCl, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Discovery client: %w", err)
|
||||
}
|
||||
|
||||
kubeCl, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
restCl, err := newRESTClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a REST client: %w", err)
|
||||
}
|
||||
|
||||
dynCl, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Dynamic client: %w", err)
|
||||
}
|
||||
|
||||
daprCl, err := daprClient.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Dapr client: %w", err)
|
||||
}
|
||||
|
||||
apiextCl, err := apiextv1.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct an API Extension client: %w", err)
|
||||
}
|
||||
|
||||
c := Client{
|
||||
|
@ -82,7 +90,7 @@ func NewClient(cfg *rest.Config, scheme *runtime.Scheme, cc ctrl.Client) (*Clien
|
|||
rest: restCl,
|
||||
}
|
||||
|
||||
c.discoveryLimiter = rate.NewLimiter(rate.Every(time.Second), 30)
|
||||
c.discoveryLimiter = rate.NewLimiter(rate.Every(time.Second), DiscoveryLimiterBurst)
|
||||
c.discoveryCache = memory.NewMemCacheClient(discoveryCl)
|
||||
c.mapper = restmapper.NewDeferredDiscoveryRESTMapper(c.discoveryCache)
|
||||
|
||||
|
@ -94,20 +102,17 @@ func newRESTClientForConfig(config *rest.Config) (*rest.RESTClient, error) {
|
|||
// so that the RESTClientFor doesn't complain
|
||||
cfg.GroupVersion = &schema.GroupVersion{}
|
||||
cfg.NegotiatedSerializer = codecs.WithoutConversion()
|
||||
|
||||
if len(cfg.UserAgent) == 0 {
|
||||
cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
|
||||
return rest.RESTClientFor(cfg)
|
||||
}
|
||||
|
||||
// IsOpenShift returns true if we are connected to a OpenShift cluster.
|
||||
func (c *Client) IsOpenShift() (bool, error) {
|
||||
if c.Discovery == nil {
|
||||
return false, nil
|
||||
rc, err := rest.RESTClientFor(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to construct a REST client: %w", err)
|
||||
}
|
||||
|
||||
return openshift.IsOpenShift(c.Discovery)
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func (c *Client) Dynamic(namespace string, obj *unstructured.Unstructured) (dynamic.ResourceInterface, error) {
|
||||
|
@ -119,7 +124,11 @@ func (c *Client) Dynamic(namespace string, obj *unstructured.Unstructured) (dyna
|
|||
|
||||
mapping, err := c.mapper.RESTMapping(obj.GroupVersionKind().GroupKind(), obj.GroupVersionKind().Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf(
|
||||
"unable to identify preferred resource mapping for %s/%s: %w",
|
||||
obj.GroupVersionKind().GroupKind(),
|
||||
obj.GroupVersionKind().Version,
|
||||
err)
|
||||
}
|
||||
|
||||
var dr dynamic.ResourceInterface
|
||||
|
|
|
@ -28,6 +28,11 @@ var (
|
|||
Log = ctrl.Log.WithName("controller")
|
||||
)
|
||||
|
||||
const (
|
||||
PprofReadTimeout = 10 * time.Second
|
||||
PprofWriteTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(Scheme))
|
||||
}
|
||||
|
@ -64,6 +69,7 @@ func Start(options Options, setup func(manager.Manager, Options) error) error {
|
|||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
return fmt.Errorf("unable to set up health check: %w", err)
|
||||
}
|
||||
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
return fmt.Errorf("unable to set up readiness check: %w", err)
|
||||
}
|
||||
|
@ -78,8 +84,8 @@ func Start(options Options, setup func(manager.Manager, Options) error) error {
|
|||
|
||||
server := &http.Server{
|
||||
Addr: options.PprofAddr,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
ReadTimeout: PprofReadTimeout,
|
||||
WriteTimeout: PprofWriteTimeout,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ func (gc *GC) Run(ctx context.Context, ns string, c *client.Client, selector lab
|
|||
}
|
||||
|
||||
func (gc *GC) deleteEachOf(ctx context.Context, c *client.Client, selector labels.Selector) (int, error) {
|
||||
|
||||
deleted := 0
|
||||
|
||||
for GVK := range gc.collectableGVKs {
|
||||
|
@ -68,11 +67,14 @@ func (gc *GC) deleteEachOf(ctx context.Context, c *client.Client, selector label
|
|||
if err := c.List(ctx, &items, options...); err != nil {
|
||||
if k8serrors.IsForbidden(err) {
|
||||
gc.l.Info("cannot gc, forbidden", "gvk", GVK.String())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
return 0, fmt.Errorf("cannot list child resources %s: %w", GVK.String(), err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -133,7 +135,7 @@ func (gc *GC) computeDeletableTypes(ctx context.Context, ns string, c *client.Cl
|
|||
// an aggregated API for custom.metrics.k8s.io that requires special
|
||||
// authentication scheme while discovering preferred resources.
|
||||
if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
|
||||
return err
|
||||
return fmt.Errorf("failure retireving supported namespaced resources: %w", err)
|
||||
}
|
||||
|
||||
// We only take types that support the "delete" verb,
|
||||
|
@ -150,22 +152,25 @@ func (gc *GC) computeDeletableTypes(ctx context.Context, ns string, c *client.Cl
|
|||
|
||||
ssrr, err = c.AuthorizationV1().SelfSubjectRulesReviews().Create(ctx, ssrr, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to create SelfSubjectRulesReviews: %w", err)
|
||||
}
|
||||
|
||||
GVKs := make(map[schema.GroupVersionKind]struct{})
|
||||
|
||||
for _, res := range apiResourceLists {
|
||||
for i := range res.APIResources {
|
||||
resourceGroup := res.APIResources[i].Group
|
||||
|
||||
if resourceGroup == "" {
|
||||
// Empty implies the group of the containing resource list should be used
|
||||
gv, err := schema.ParseGroupVersion(res.GroupVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failure creating SelfSubjectRulesReview: %w", err)
|
||||
}
|
||||
|
||||
resourceGroup = gv.Group
|
||||
}
|
||||
|
||||
rule:
|
||||
for _, rule := range ssrr.Status.ResourceRules {
|
||||
if !slices.Contains(rule.Verbs, "delete") && !slices.Contains(rule.Verbs, "*") {
|
||||
|
@ -179,6 +184,7 @@ func (gc *GC) computeDeletableTypes(ctx context.Context, ns string, c *client.Cl
|
|||
if gc.canBeDeleted(ctx, GVK) {
|
||||
GVKs[GVK] = struct{}{}
|
||||
}
|
||||
|
||||
break rule
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ func (p AnnotationChanged) Update(e event.UpdateEvent) bool {
|
|||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
|
||||
if e.ObjectOld.GetAnnotations() == nil {
|
||||
log.Error(nil, "Update event has no old object annotations to update", "event", e)
|
||||
return false
|
||||
|
|
|
@ -33,6 +33,7 @@ func (p HasLabel) test(obj client.Object) bool {
|
|||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if obj.GetLabels() == nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ func (p StatusChanged) Update(e event.UpdateEvent) bool {
|
|||
log.Error(nil, "Update event has no old object to update", "event", e)
|
||||
return false
|
||||
}
|
||||
|
||||
if e.ObjectNew == nil {
|
||||
log.Error(nil, "Update event has no new object to update", "event", e)
|
||||
return false
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package customizers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/itchyny/gojq"
|
||||
)
|
||||
|
||||
|
@ -8,7 +10,7 @@ func JQ(expression string) func(map[string]any) (map[string]any, error) {
|
|||
return func(in map[string]any) (map[string]any, error) {
|
||||
query, err := gojq.Parse(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to parse expression %s: %w", expression, err)
|
||||
}
|
||||
|
||||
it := query.Run(in)
|
||||
|
@ -23,7 +25,7 @@ func JQ(expression string) func(map[string]any) (map[string]any, error) {
|
|||
}
|
||||
|
||||
if r, ok := v.(map[string]any); ok {
|
||||
return r, err
|
||||
return r, nil
|
||||
}
|
||||
|
||||
return in, nil
|
|
@ -60,10 +60,15 @@ func (e *Engine) Customizer(customizer ValuesCustomizer, customizers ...ValuesCu
|
|||
func (e *Engine) Load(options ChartOptions) (*chart.Chart, error) {
|
||||
path, err := options.LocateChart(options.Name, e.env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load chart (repo: %s, name: %s, version: %s), reson: %w", options.RepoURL, options.Name, options.Version, err)
|
||||
return nil, fmt.Errorf("unable to load chart (repo: %s, name: %s, version: %s): %w", options.RepoURL, options.Name, options.Version, err)
|
||||
}
|
||||
|
||||
return loader.Load(path)
|
||||
c, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load chart (repo: %s, name: %s, version: %s): %w", options.RepoURL, options.Name, options.Version, err)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (e *Engine) Render(c *chart.Chart, dapr *daprApi.DaprInstance, overrides map[string]interface{}) ([]unstructured.Unstructured, error) {
|
||||
|
@ -78,6 +83,7 @@ func (e *Engine) Render(c *chart.Chart, dapr *daprApi.DaprInstance, overrides ma
|
|||
}
|
||||
|
||||
keys := make([]string, 0, len(files))
|
||||
|
||||
for k := range files {
|
||||
if !strings.HasSuffix(k, ".yaml") && !strings.HasSuffix(k, ".yml") {
|
||||
continue
|
||||
|
@ -92,10 +98,12 @@ func (e *Engine) Render(c *chart.Chart, dapr *daprApi.DaprInstance, overrides ma
|
|||
|
||||
for _, k := range keys {
|
||||
v := files[k]
|
||||
|
||||
ul, err := resources.Decode(e.decoder, []byte(v))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot decode %s: %w", k, err)
|
||||
}
|
||||
|
||||
if ul == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -152,5 +160,4 @@ func (e *Engine) renderValues(
|
|||
}
|
||||
|
||||
return rv, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/selection"
|
||||
)
|
||||
|
||||
//nolint:wrapcheck
|
||||
func ReleaseSelector() (labels.Selector, error) {
|
||||
hasReleaseNameLabel, err := labels.NewRequirement(ReleaseName, selection.Exists, []string{})
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package openshift
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
@ -14,8 +16,10 @@ func IsOpenShift(client discovery.DiscoveryInterface) (bool, error) {
|
|||
_, err := client.ServerResourcesForGroupVersion("route.openshift.io/v1")
|
||||
if err != nil && k8serrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to determine if the Kubernetes distro is OpenShift: %w", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
|
|
@ -108,6 +108,7 @@ func ToUnstructured(s *runtime.Scheme, obj runtime.Object) (*unstructured.Unstru
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert to unstructured - unable to get GVK %w", err)
|
||||
}
|
||||
|
||||
apiv, k := gvks[0].ToAPIVersionAndKind()
|
||||
|
||||
u.SetAPIVersion(apiv)
|
||||
|
@ -133,19 +134,20 @@ func Decode(decoder runtime.Decoder, content []byte) ([]unstructured.Unstructure
|
|||
break
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to decode resource: %w", err)
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if out["Kind"] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
encoded, err := yaml.Marshal(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to marshal resource: %w", err)
|
||||
}
|
||||
|
||||
var obj unstructured.Unstructured
|
||||
|
@ -155,11 +157,10 @@ func Decode(decoder runtime.Decoder, content []byte) ([]unstructured.Unstructure
|
|||
continue
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to decode resource: %w", err)
|
||||
}
|
||||
|
||||
results = append(results, obj)
|
||||
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
|
|
@ -10,6 +10,14 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultProbeInitialDelay = 5
|
||||
DefaultProbePeriod = 10
|
||||
DefaultProbeTimeout = 10
|
||||
DefaultProbeFailureThreshold = 10
|
||||
DefaultProbeSuccessThreshold = 1
|
||||
)
|
||||
|
||||
func WithOwnerReference(object client.Object) *metav1ac.OwnerReferenceApplyConfiguration {
|
||||
return metav1ac.OwnerReference().
|
||||
WithAPIVersion(object.GetObjectKind().GroupVersionKind().GroupVersion().String()).
|
||||
|
@ -22,11 +30,11 @@ func WithOwnerReference(object client.Object) *metav1ac.OwnerReferenceApplyConfi
|
|||
|
||||
func WithHTTPProbe(path string, port int32) *corev1ac.ProbeApplyConfiguration {
|
||||
return corev1ac.Probe().
|
||||
WithInitialDelaySeconds(5).
|
||||
WithPeriodSeconds(10).
|
||||
WithFailureThreshold(10).
|
||||
WithSuccessThreshold(1).
|
||||
WithTimeoutSeconds(10).
|
||||
WithInitialDelaySeconds(DefaultProbeInitialDelay).
|
||||
WithPeriodSeconds(DefaultProbePeriod).
|
||||
WithFailureThreshold(DefaultProbeFailureThreshold).
|
||||
WithSuccessThreshold(DefaultProbeSuccessThreshold).
|
||||
WithTimeoutSeconds(DefaultProbeTimeout).
|
||||
WithHTTPGet(corev1ac.HTTPGetAction().
|
||||
WithPath(path).
|
||||
WithPort(intstr.IntOrString{IntVal: port}).
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package maputils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
)
|
||||
|
||||
|
@ -18,23 +16,9 @@ func Merge(dst map[string]interface{}, source map[string]interface{}) map[string
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
out[k] = v
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func Lookup(m map[string]interface{}, ks ...string) (interface{}, error) {
|
||||
if len(ks) == 0 { // degenerate input
|
||||
return nil, errors.New("lookup needs at least one key")
|
||||
}
|
||||
if rval, ok := m[ks[0]]; !ok {
|
||||
return nil, fmt.Errorf("key not found; remaining keys: %v", ks)
|
||||
} else if len(ks) == 1 { // we've reached the final key
|
||||
return rval, nil
|
||||
} else if m, ok := rval.(map[string]interface{}); !ok {
|
||||
return nil, fmt.Errorf("malformed structure at %#v", rval)
|
||||
} else { // 1+ more keys
|
||||
return Lookup(m, ks[1:]...)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
IngressPort = 8081
|
||||
)
|
||||
|
||||
func ValidateDaprApp(test support.Test, namespace string) {
|
||||
test.T().Helper()
|
||||
|
||||
|
@ -52,7 +56,7 @@ func ValidateDaprApp(test support.Test, namespace string) {
|
|||
|
||||
test.T().Logf("Testing the app with name %s", appName)
|
||||
|
||||
base := fmt.Sprintf("http://localhost:%d/%s", 8081, appName)
|
||||
base := fmt.Sprintf("http://localhost:%d/%s", IngressPort, appName)
|
||||
value := xid.New().String()
|
||||
|
||||
//nolint:bodyclose
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/internal/controller/operator/controlplane"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/dapr"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/olm"
|
||||
|
||||
daprAc "github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/client/applyconfiguration/operator/v1alpha1"
|
||||
daprTC "github.com/dapr-sandbox/dapr-kubernetes-operator/test/e2e/common"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
. "github.com/dapr-sandbox/dapr-kubernetes-operator/test/support"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestDaprDeployWithControlPlane(t *testing.T) {
|
||||
test := With(t)
|
||||
|
||||
ns := test.NewTestNamespace()
|
||||
image := os.Getenv("CATALOG_CONTAINER_IMAGE")
|
||||
|
||||
test.Expect(image).
|
||||
ToNot(BeEmpty())
|
||||
|
||||
//
|
||||
// Install Operator
|
||||
//
|
||||
|
||||
olm.DeployOperator(test, ns, image)
|
||||
|
||||
//
|
||||
// Control plane
|
||||
//
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-control-plane", ns.Name), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr
|
||||
//
|
||||
|
||||
res := dapr.DeployControlPlane(
|
||||
test,
|
||||
daprAc.DaprControlPlaneSpec().
|
||||
WithValues(dapr.Values(test, map[string]interface{}{
|
||||
// enable pod watchdog as sometimes the sidecar for some
|
||||
// (yet) unknown reason is not injected when the pod is
|
||||
// created, hence the dapr app won't properly start up
|
||||
"dapr_operator": map[string]interface{}{
|
||||
"watchInterval": "1s",
|
||||
},
|
||||
})),
|
||||
dapr.WithControlPlaneName(controlplane.DaprControlPlaneResourceName),
|
||||
dapr.WithControlPlaneNamespace(ns.Name),
|
||||
)
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-operator", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sentry", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sidecar-injector", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr Application
|
||||
//
|
||||
|
||||
daprTC.ValidateDaprApp(test, res.Namespace)
|
||||
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/internal/controller/operator/instance"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/dapr"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/olm"
|
||||
|
||||
daprAc "github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/client/applyconfiguration/operator/v1alpha1"
|
||||
daprTC "github.com/dapr-sandbox/dapr-kubernetes-operator/test/e2e/common"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
. "github.com/dapr-sandbox/dapr-kubernetes-operator/test/support"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestDaprDeployWithInstance(t *testing.T) {
|
||||
test := With(t)
|
||||
|
||||
ns := test.NewTestNamespace()
|
||||
image := os.Getenv("CATALOG_CONTAINER_IMAGE")
|
||||
|
||||
test.Expect(image).
|
||||
ToNot(BeEmpty())
|
||||
|
||||
//
|
||||
// Install Operator
|
||||
//
|
||||
|
||||
olm.DeployOperator(test, ns, image)
|
||||
|
||||
//
|
||||
// Control plane
|
||||
//
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-control-plane", ns.Name), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr
|
||||
//
|
||||
|
||||
res := dapr.DeployInstance(
|
||||
test,
|
||||
daprAc.DaprInstanceSpec().
|
||||
WithValues(dapr.Values(test, map[string]interface{}{
|
||||
// enable pod watchdog as sometimes the sidecar for some
|
||||
// (yet) unknown reason is not injected when the pod is
|
||||
// created, hence the dapr app won't properly start up
|
||||
"dapr_operator": map[string]interface{}{
|
||||
"watchInterval": "1s",
|
||||
},
|
||||
})),
|
||||
dapr.WithInstanceName(instance.DaprInstanceResourceName),
|
||||
dapr.WithInstanceNamespace(ns.Name),
|
||||
)
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-operator", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sentry", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sidecar-injector", res.Namespace), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr Application
|
||||
//
|
||||
|
||||
daprTC.ValidateDaprApp(test, res.Namespace)
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/internal/controller/operator/controlplane"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/internal/controller/operator/instance"
|
||||
daprAc "github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/client/applyconfiguration/operator/v1alpha1"
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/dapr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/test/support/olm"
|
||||
|
||||
daprTC "github.com/dapr-sandbox/dapr-kubernetes-operator/test/e2e/common"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
. "github.com/dapr-sandbox/dapr-kubernetes-operator/test/support"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestDaprDeploy(t *testing.T) {
|
||||
t.Run("With ControlPlane", func(t *testing.T) {
|
||||
testDaprDeploy(
|
||||
With(t),
|
||||
func(test Test, ns *corev1.Namespace) client.Object {
|
||||
return dapr.DeployControlPlane(
|
||||
test,
|
||||
daprAc.DaprControlPlaneSpec().
|
||||
WithValues(dapr.Values(test, map[string]interface{}{
|
||||
// enable pod watchdog as sometimes the sidecar for some
|
||||
// (yet) unknown reason is not injected when the pod is
|
||||
// created, hence the dapr app won't properly start up
|
||||
"dapr_operator": map[string]interface{}{
|
||||
"watchInterval": "1s",
|
||||
},
|
||||
})),
|
||||
dapr.WithControlPlaneName(controlplane.DaprControlPlaneResourceName),
|
||||
dapr.WithControlPlaneNamespace(ns.Name),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("With Instance", func(t *testing.T) {
|
||||
testDaprDeploy(
|
||||
With(t),
|
||||
func(test Test, ns *corev1.Namespace) client.Object {
|
||||
return dapr.DeployInstance(
|
||||
test,
|
||||
daprAc.DaprInstanceSpec().
|
||||
WithValues(dapr.Values(test, map[string]interface{}{
|
||||
// enable pod watchdog as sometimes the sidecar for some
|
||||
// (yet) unknown reason is not injected when the pod is
|
||||
// created, hence the dapr app won't properly start up
|
||||
"dapr_operator": map[string]interface{}{
|
||||
"watchInterval": "1s",
|
||||
},
|
||||
})),
|
||||
dapr.WithInstanceName(instance.DaprInstanceResourceName),
|
||||
dapr.WithInstanceNamespace(ns.Name),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func testDaprDeploy(test Test, f func(t Test, ns *corev1.Namespace) client.Object) {
|
||||
test.T().Helper()
|
||||
|
||||
ns := test.NewTestNamespace()
|
||||
image := os.Getenv("CATALOG_CONTAINER_IMAGE")
|
||||
|
||||
test.Expect(image).
|
||||
ToNot(BeEmpty())
|
||||
|
||||
//
|
||||
// Install Operator
|
||||
//
|
||||
|
||||
olm.DeployOperator(test, ns, image)
|
||||
|
||||
//
|
||||
// Control plane
|
||||
//
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-control-plane", ns.Name), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr
|
||||
//
|
||||
|
||||
res := f(test, ns)
|
||||
|
||||
test.Eventually(Deployment(test, "dapr-operator", res.GetNamespace()), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sentry", res.GetNamespace()), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
test.Eventually(Deployment(test, "dapr-sidecar-injector", res.GetNamespace()), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionStatus(appsv1.DeploymentAvailable), Equal(corev1.ConditionTrue)))
|
||||
|
||||
//
|
||||
// Dapr Application
|
||||
//
|
||||
|
||||
daprTC.ValidateDaprApp(test, res.GetNamespace())
|
||||
}
|
|
@ -69,5 +69,4 @@ func TestDaprControlPlaneDeployWrongCR(t *testing.T) {
|
|||
WithTransform(ConditionStatus(conditions.TypeReconciled), Equal(corev1.ConditionFalse)))
|
||||
test.Eventually(dapr.ControlPlane(test, instance), TestTimeoutLong).Should(
|
||||
WithTransform(ConditionReason(conditions.TypeReconciled), Equal(conditions.ReasonUnsupportedConfiguration)))
|
||||
|
||||
}
|
||||
|
|
|
@ -128,7 +128,6 @@ func TestDaprInstanceDeployWithCustomSidecarImage(t *testing.T) {
|
|||
jq.Match(`.items[0].spec.containers[0].env[] | select(.name == "SIDECAR_IMAGE_PULL_POLICY") | .value == "%s"`, corev1.PullAlways),
|
||||
)),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func TestDaprInstanceDeployWithApp(t *testing.T) {
|
||||
|
|
|
@ -19,6 +19,10 @@ var (
|
|||
once sync.Once
|
||||
)
|
||||
|
||||
const (
|
||||
HTTPReadHeaderTimeout = 3 * time.Second
|
||||
)
|
||||
|
||||
func init() {
|
||||
appPort = os.Getenv("APP_PORT")
|
||||
if appPort == "" {
|
||||
|
@ -110,7 +114,7 @@ func main() {
|
|||
|
||||
server := &http.Server{
|
||||
Addr: ":" + appPort,
|
||||
ReadHeaderTimeout: 3 * time.Second,
|
||||
ReadHeaderTimeout: HTTPReadHeaderTimeout,
|
||||
}
|
||||
|
||||
err := server.ListenAndServe()
|
||||
|
|
|
@ -39,34 +39,37 @@ func New(t *testing.T) (*Client, error) {
|
|||
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to get Kubernetes config: %w", err)
|
||||
}
|
||||
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Discovery client: %w", err)
|
||||
}
|
||||
|
||||
kubeClient, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Kubernetes client: %w", err)
|
||||
}
|
||||
extClient, err := apiextClient.NewForConfig(cfg)
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Dynamic client: %w", err)
|
||||
}
|
||||
|
||||
dpClient, err := daprClient.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct a Dapr client: %w", err)
|
||||
}
|
||||
|
||||
extClient, err := apiextClient.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to construct an API Extension client: %w", err)
|
||||
}
|
||||
|
||||
oClient, err := olmAC.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to construct an OLM client: %w", err)
|
||||
}
|
||||
|
||||
c := Client{
|
||||
|
@ -102,12 +105,12 @@ func (c *Client) Scheme() *runtime.Scheme {
|
|||
func (c *Client) RESTMapper() (meta.RESTMapper, error) {
|
||||
gr, err := restmapper.GetAPIGroupResources(c.Discovery())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error computing API/Group resources, %w", err)
|
||||
}
|
||||
|
||||
return restmapper.NewDiscoveryRESTMapper(gr), nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) ForResource(in *unstructured.Unstructured) (dynamic.ResourceInterface, error) {
|
||||
gvk := in.GetObjectKind().GroupVersionKind()
|
||||
gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}
|
||||
|
|
|
@ -2,6 +2,7 @@ package dapr
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/pointer"
|
||||
|
@ -25,6 +26,11 @@ import (
|
|||
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
TestAppPort = 8080
|
||||
TestAppServicePort = 80
|
||||
)
|
||||
|
||||
func DeployTestApp(t support.Test, name string, namespace string) {
|
||||
t.T().Helper()
|
||||
t.T().Logf("Setting up Dapr Application %s in namespace %s", name, namespace)
|
||||
|
@ -103,7 +109,7 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
}).
|
||||
WithAnnotations(map[string]string{
|
||||
"dapr.io/app-id": name,
|
||||
"dapr.io/app-port": "8080",
|
||||
"dapr.io/app-port": strconv.Itoa(TestAppPort),
|
||||
"dapr.io/enabled": "true",
|
||||
"dapr.io/enable-api-logging": "true",
|
||||
}).
|
||||
|
@ -112,9 +118,9 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
WithImage("kind.local/dapr-test-app:latest").
|
||||
WithImagePullPolicy(corev1.PullNever).
|
||||
WithName("app").
|
||||
WithPorts(resources.WithPort("http", 8080)).
|
||||
WithReadinessProbe(resources.WithHTTPProbe("/health/readiness", 8080)).
|
||||
WithLivenessProbe(resources.WithHTTPProbe("/health/liveness", 8080)).
|
||||
WithPorts(resources.WithPort("http", TestAppPort)).
|
||||
WithReadinessProbe(resources.WithHTTPProbe("/health/readiness", TestAppPort)).
|
||||
WithLivenessProbe(resources.WithHTTPProbe("/health/liveness", TestAppPort)).
|
||||
WithEnv(resources.WithEnv("STATESTORE_NAME", name)),
|
||||
),
|
||||
),
|
||||
|
@ -148,8 +154,8 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
WithPorts(corev1ac.ServicePort().
|
||||
WithName("http").
|
||||
WithProtocol(corev1.ProtocolTCP).
|
||||
WithPort(80).
|
||||
WithTargetPort(intstr.FromInt32(8080))).
|
||||
WithPort(TestAppServicePort).
|
||||
WithTargetPort(intstr.FromInt32(TestAppPort))).
|
||||
WithSelector(map[string]string{
|
||||
"app": name,
|
||||
}).
|
||||
|
@ -177,6 +183,7 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(path, "(/|$)(.*)") {
|
||||
path += "(/|$)(.*)"
|
||||
}
|
||||
|
@ -198,7 +205,7 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
WithService(netv1ac.IngressServiceBackend().
|
||||
WithName(name).
|
||||
WithPort(netv1ac.ServiceBackendPort().
|
||||
WithNumber(80),
|
||||
WithNumber(TestAppServicePort),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -220,7 +227,6 @@ func DeployTestApp(t support.Test, name string, namespace string) {
|
|||
t.Cleanup(func() []runtime.Object {
|
||||
return []runtime.Object{ing}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Values(t support.Test, in map[string]any) *daprAc.JSONApplyConfiguration {
|
||||
|
@ -239,5 +245,4 @@ func Values(t support.Test, in map[string]any) *daprAc.JSONApplyConfiguration {
|
|||
cfg.RawMessage = data
|
||||
|
||||
return cfg
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ func GET(t support.Test, url string) func(g gomega.Gomega) (*http.Response, erro
|
|||
}
|
||||
|
||||
func POST(t support.Test, url string, contentType string, content []byte) func(g gomega.Gomega) (*http.Response, error) {
|
||||
|
||||
return func(g gomega.Gomega) (*http.Response, error) {
|
||||
data := content
|
||||
if data == nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
|
@ -22,7 +23,6 @@ func WithLog(value func(string, ...interface{})) ConfigurationOption {
|
|||
}
|
||||
|
||||
func New(options ...ConfigurationOption) (*Helm, error) {
|
||||
|
||||
settings := cli.New()
|
||||
config := action.Configuration{}
|
||||
|
||||
|
@ -44,7 +44,7 @@ func New(options ...ConfigurationOption) (*Helm, error) {
|
|||
|
||||
err = config.Init(settings.RESTClientGetter(), settings.Namespace(), "memory", config.Log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to initialize action config: %w", err)
|
||||
}
|
||||
|
||||
h := Helm{
|
||||
|
|
|
@ -11,6 +11,10 @@ import (
|
|||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultInstallTimeout = 10 * time.Minute
|
||||
)
|
||||
|
||||
type InstallOption func(*ReleaseOptions[action.Install])
|
||||
|
||||
func (h *Helm) Install(ctx context.Context, chart string, options ...InstallOption) (*release.Release, error) {
|
||||
|
@ -21,7 +25,7 @@ func (h *Helm) Install(ctx context.Context, chart string, options ...InstallOpti
|
|||
client.IncludeCRDs = true
|
||||
client.Wait = true
|
||||
client.Namespace = xid.New().String()
|
||||
client.Timeout = 10 * time.Minute
|
||||
client.Timeout = DefaultInstallTimeout
|
||||
|
||||
io := ReleaseOptions[action.Install]{
|
||||
Client: client,
|
||||
|
|
|
@ -2,6 +2,7 @@ package helm
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
)
|
||||
|
@ -24,7 +25,7 @@ func (h *Helm) Uninstall(_ context.Context, name string, options ...UninstallOpt
|
|||
|
||||
_, err := client.Run(name)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unabele to uninstall release %s: %w", name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -17,11 +17,14 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
CatalogRegistryPollInterval = 10 * time.Minute
|
||||
)
|
||||
|
||||
func DeployOperator(test support.Test, ns *corev1.Namespace, image string) {
|
||||
//
|
||||
// Install OperatorGroups
|
||||
//
|
||||
|
||||
og, err := test.Client().OLM().OperatorsV1().OperatorGroups(ns.Name).Create(
|
||||
test.Ctx(),
|
||||
&olmV1.OperatorGroup{
|
||||
|
@ -91,7 +94,7 @@ func DeployOperator(test support.Test, ns *corev1.Namespace, image string) {
|
|||
UpdateStrategy: &olmV1Alpha1.UpdateStrategy{
|
||||
RegistryPoll: &olmV1Alpha1.RegistryPoll{
|
||||
Interval: &metav1.Duration{
|
||||
Duration: 10 * time.Minute,
|
||||
Duration: CatalogRegistryPollInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -172,9 +175,11 @@ func OperatorGroup(t support.Test, name string, namespace string) func(g gomega.
|
|||
name,
|
||||
metav1.GetOptions{},
|
||||
)
|
||||
|
||||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil && k8serrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package support
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
|
@ -14,13 +13,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
TestTimeoutMini = 5 * time.Second
|
||||
TestTimeoutShort = 1 * time.Minute
|
||||
TestTimeoutMedium = 2 * time.Minute
|
||||
TestTimeoutLong = 5 * time.Minute
|
||||
)
|
||||
|
||||
func runCleanup(t Test, in runtime.Object) error {
|
||||
un, err := resources.ToUnstructured(t.Client().Scheme(), in)
|
||||
if err != nil {
|
||||
|
@ -37,6 +29,7 @@ func runCleanup(t Test, in runtime.Object) error {
|
|||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return fmt.Errorf("failed to get current object, %w", err)
|
||||
}
|
||||
|
||||
if err != nil && k8serrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ func Resource(t Test, ri dynamic.ResourceInterface, un *unstructured.Unstructure
|
|||
if err != nil && !k8serrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil && k8serrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package support
|
||||
|
||||
import (
|
||||
"github.com/dapr-sandbox/dapr-kubernetes-operator/pkg/pointer"
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/rs/xid"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -29,9 +30,10 @@ func createTestNamespace(t Test, options ...Option[*corev1.Namespace]) *corev1.N
|
|||
|
||||
func deleteTestNamespace(t Test, namespace *corev1.Namespace) {
|
||||
t.T().Helper()
|
||||
propagationPolicy := metav1.DeletePropagationBackground
|
||||
|
||||
err := t.Client().CoreV1().Namespaces().Delete(t.Ctx(), namespace.Name, metav1.DeleteOptions{
|
||||
PropagationPolicy: &propagationPolicy,
|
||||
PropagationPolicy: pointer.Any(metav1.DeletePropagationBackground),
|
||||
})
|
||||
|
||||
t.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
|
|
|
@ -28,13 +28,27 @@ import (
|
|||
olmV1Alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
TestTimeoutMini = 5 * time.Second
|
||||
TestTimeoutShort = 1 * time.Minute
|
||||
TestTimeoutMedium = 2 * time.Minute
|
||||
TestTimeoutLong = 5 * time.Minute
|
||||
|
||||
DefaultEventuallyPollingInterval = 500 * time.Millisecond
|
||||
DefaultEventuallyTimeout = TestTimeoutLong
|
||||
DefaultConsistentlyDuration = 500 * time.Millisecond
|
||||
DefaultConsistentlyPollingInterval = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := daprApi.AddToScheme(scheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := olmV1.AddToScheme(scheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := olmV1Alpha1.AddToScheme(scheme.Scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -47,13 +61,13 @@ type Test interface {
|
|||
Ctx() context.Context
|
||||
|
||||
ID() string
|
||||
Cleanup(func() []runtime.Object)
|
||||
Cleanup(f func() []runtime.Object)
|
||||
|
||||
Client() *supportclient.Client
|
||||
Helm() *helmsupport.Helm
|
||||
HTTPClient() *http.Client
|
||||
|
||||
NewTestNamespace(...Option[*corev1.Namespace]) *corev1.Namespace
|
||||
NewTestNamespace(opts ...Option[*corev1.Namespace]) *corev1.Namespace
|
||||
}
|
||||
|
||||
type Option[T any] interface {
|
||||
|
@ -70,8 +84,10 @@ func With(t *testing.T) Test {
|
|||
if deadline, ok := t.Deadline(); ok {
|
||||
withDeadline, cancel := context.WithDeadline(ctx, deadline)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
ctx = withDeadline
|
||||
}
|
||||
|
||||
answer := &T{
|
||||
WithT: gomega.NewWithT(t),
|
||||
id: xid.New().String(),
|
||||
|
@ -81,10 +97,10 @@ func With(t *testing.T) Test {
|
|||
cleanup: make([]func() []runtime.Object, 0),
|
||||
}
|
||||
|
||||
answer.SetDefaultEventuallyPollingInterval(500 * time.Millisecond)
|
||||
answer.SetDefaultEventuallyTimeout(TestTimeoutLong)
|
||||
answer.SetDefaultConsistentlyDuration(500 * time.Millisecond)
|
||||
answer.SetDefaultConsistentlyDuration(TestTimeoutLong)
|
||||
answer.SetDefaultEventuallyPollingInterval(DefaultEventuallyPollingInterval)
|
||||
answer.SetDefaultEventuallyTimeout(DefaultEventuallyTimeout)
|
||||
answer.SetDefaultConsistentlyDuration(DefaultConsistentlyDuration)
|
||||
answer.SetDefaultConsistentlyPollingInterval(DefaultConsistentlyPollingInterval)
|
||||
|
||||
t.Cleanup(func() {
|
||||
t.Log("Run Test cleanup")
|
||||
|
@ -146,8 +162,10 @@ func (t *T) Client() *supportclient.Client {
|
|||
if err != nil {
|
||||
t.T().Fatalf("Error creating client: %v", err)
|
||||
}
|
||||
|
||||
t.client = c
|
||||
})
|
||||
|
||||
return t.client
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue