Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
4485aa4
feat: wip - fill out v1beta1 structs
arturshadnik Aug 28, 2025
86bf83c
wip: working hub init
arturshadnik Sep 9, 2025
2a5bc4f
chore: make reviewable
arturshadnik Sep 9, 2025
08832b2
refactor: conditions
arturshadnik Sep 9, 2025
29987a1
fix: implement spoke cleanup
arturshadnik Sep 9, 2025
2803c9a
wip: implement addons
arturshadnik Sep 10, 2025
aa08e87
chore: make reviewable
arturshadnik Sep 10, 2025
fd3dcad
chore: make reviewable
arturshadnik Sep 10, 2025
746f862
chore: fix test data
arturshadnik Sep 10, 2025
6244ee0
feat: background delete spokes during hub deletion
arturshadnik Sep 10, 2025
7c44c46
fix: allow parallel spoke reconciles; actually do hub cleanup
arturshadnik Sep 10, 2025
f7806d0
refactor: simplify addon conds; disable hubAddons during cleanup
arturshadnik Sep 10, 2025
bf4356f
refactor: dont fail if hub not found
arturshadnik Sep 10, 2025
9605565
feat: allow global and per-spoke klusterlet values
arturshadnik Sep 11, 2025
067d30e
refactor: use 3rd party merge lib; set managed spoke fields
arturshadnik Sep 11, 2025
06fc5b7
chore: make reviewable
arturshadnik Sep 11, 2025
5f1ff8b
refactor: get addon state from cluster not status
arturshadnik Sep 11, 2025
a1707de
fix: tweak hub addon reconcile
arturshadnik Sep 11, 2025
88f626a
chore: prevent flooding stdout
arturshadnik Sep 11, 2025
c3c71f3
feat: watch resource updates to speed up syncs
arturshadnik Sep 11, 2025
4eb6d11
chore: make reviewable
arturshadnik Sep 11, 2025
150fc36
fix: spelling
arturshadnik Sep 12, 2025
cdc32f1
feat: webhooks
arturshadnik Sep 12, 2025
0243f24
chore: some rabbit comments
arturshadnik Sep 12, 2025
47d86dd
fix: dont append empty annotations
arturshadnik Sep 12, 2025
c8b0322
chore: update validation, skip redundant hub cleanup checks
arturshadnik Sep 13, 2025
6eff13a
chore: tighten spoke reconcile predicate
arturshadnik Sep 13, 2025
ed0a7c6
chore: make reviewable
arturshadnik Sep 13, 2025
7eefdeb
chore: add kubeconfig validation on update
arturshadnik Sep 15, 2025
6aecfb2
chore: add kubeconfig validation on update
arturshadnik Sep 15, 2025
f4511e1
refactor: many things
arturshadnik Sep 15, 2025
bf6bf15
chore: error handling
arturshadnik Sep 16, 2025
4643f71
fix: update event map for enqueue
arturshadnik Sep 16, 2025
ba02421
chore: tweak enqueue mapping
arturshadnik Sep 16, 2025
7e83ca8
chore: allow apiServer updates
arturshadnik Sep 16, 2025
e3d450d
fix: guard against nil hub in spoke upgrade; only delete spokes manag…
arturshadnik Sep 16, 2025
129bbed
chore: nil check
arturshadnik Sep 16, 2025
b2ff694
feat: namespaced hub
arturshadnik Sep 17, 2025
ef89a47
feat: v1beta1 secret namespace
arturshadnik Sep 18, 2025
17d41a3
refactor: fix resource interface
arturshadnik Sep 18, 2025
d09a867
test: fix int test scaffolds
arturshadnik Sep 18, 2025
7b73c91
test: add controller integration tests
arturshadnik Sep 18, 2025
7316a3b
test: add webhook int tests
arturshadnik Sep 18, 2025
2e3e861
chore: delete unused hub defaulting webhook
arturshadnik Sep 18, 2025
a2db669
test: add some unit tests
arturshadnik Sep 18, 2025
a663204
feat: update helm webhook configs
arturshadnik Sep 18, 2025
a351d4a
chore: dont requeue spoke if hub timeout/verbosity updated
arturshadnik Sep 18, 2025
34956a0
fix: remove defaulter, set overrides in controller
arturshadnik Sep 18, 2025
9a32b4a
chore: helm chart
arturshadnik Sep 19, 2025
ce7ba4f
fix: helm template, values
arturshadnik Sep 19, 2025
92454b1
fix: tweak helm chart
arturshadnik Sep 19, 2025
6b93ba7
fix: guard against nil configmap ref
arturshadnik Sep 19, 2025
67c5162
test: WIP - add e2e tests
arturshadnik Sep 19, 2025
cdd5f99
test: reintroduce v1alpha1 suite
arturshadnik Sep 19, 2025
a84e71b
chore: make reviewable
arturshadnik Sep 19, 2025
3e52951
test: fix v1alpha1 tests; update chart
arturshadnik Sep 19, 2025
193a894
ci: label filters
arturshadnik Sep 22, 2025
6b2a5ee
ci: change label filter collector logic
arturshadnik Sep 22, 2025
a435735
fix: var name
arturshadnik Sep 22, 2025
f1ed7b5
test: update test helper
arturshadnik Sep 22, 2025
1f7ac98
chore: validation wording
arturshadnik Sep 22, 2025
aca5865
ci: only run planner on pull_request_target
arturshadnik Sep 22, 2025
1fe2e37
chore: rabbit
arturshadnik Sep 22, 2025
0581ab0
feat: long-lived addon clientset for webhooks
arturshadnik Sep 22, 2025
a63fc4e
fix: add back pull_request event for now
arturshadnik Sep 22, 2025
458d731
test: update tests
arturshadnik Sep 22, 2025
97a64fa
chore: enforce hub one-of condition
arturshadnik Sep 22, 2025
909e45e
chore: move code around
arturshadnik Sep 22, 2025
56f697c
chore: address review comments
arturshadnik Sep 23, 2025
8b57afa
chore: default labels
arturshadnik Sep 23, 2025
ba77a5a
ci: always apply labels
arturshadnik Sep 23, 2025
b9dd221
fix: dbl pointer
arturshadnik Sep 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
artifacts:
required: false
type: string
ginkgoLabelFilter:
required: false
type: string

env:
GO_REQUIRED_MIN_VERSION: ''
Expand All @@ -33,7 +36,8 @@ jobs:

- name: Test E2E
run: |
cd ${{ inputs.repo }} && make test-e2e
cd ${{ inputs.repo }}
LABEL_FILTER=${{ inputs.ginkgoLabelFilter }} make test-e2e

- name: Upload Artifacts
if: |
Expand Down
51 changes: 49 additions & 2 deletions .github/workflows/planner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Planner
on:
pull_request:
pull_request_target:
types: [unlabeled]
types: [opened, labeled, unlabeled, reopened, synchronize, ready_for_review]
workflow_dispatch:

concurrency:
Expand Down Expand Up @@ -92,6 +92,50 @@ jobs:

echo "Matrix: $matrixJson"
echo "Artifacts: $artifacts_json"

extract-label-filter:
name: extract-label-filter
needs: generate-matrix
runs-on: ubuntu-latest
outputs:
ginkgoLabelFilter: ${{ steps.extract-label-filter.outputs.ginkgoLabelFilter }}
steps:
- name: Extract label filter
id: extract-label-filter
run: |
set -e
# Find labels that start with 'ginkgo-filter:'
LABELS="${{ join(github.event.pull_request.labels.*.name, ',') }}"

# Array to collect all filters
FILTERS=()

# Extract all ginkgo filters
for label in $(echo $LABELS | tr ',' '\n'); do
if [[ $label == ginkgo-filter:* ]]; then
# Extract the filter part after the prefix
FILTER="${label#ginkgo-filter:}"
echo "Found Ginkgo filter in label: $FILTER"
FILTERS+=("$FILTER")
fi
done

# If we have filters, combine them with OR operator
if [ ${#FILTERS[@]} -gt 0 ]; then
COMBINED_FILTER=""

# OR each filter
for i in "${!FILTERS[@]}"; do
if [ $i -eq 0 ]; then
COMBINED_FILTER="(${FILTERS[$i]})"
else
COMBINED_FILTER="$COMBINED_FILTER||(${FILTERS[$i]})"
fi
done

echo "Final ginkgo label filter: $COMBINED_FILTER"
echo "ginkgoLabelFilter=$COMBINED_FILTER" >> $GITHUB_OUTPUT
fi

call-test:
name: test
Expand All @@ -108,7 +152,9 @@ jobs:

call-e2e:
name: e2e
needs: generate-matrix
needs:
- generate-matrix
- extract-label-filter
if: |
needs.generate-matrix.outputs.matrix != ''
strategy:
Expand All @@ -118,4 +164,5 @@ jobs:
with:
repo: ${{ matrix.repo }}
artifacts: ${{ fromJson(needs.generate-matrix.outputs.artifacts)[matrix.repo] }}
ginkgoLabelFilter: ${{ needs.extract-label-filter.outputs.ginkgoLabelFilter }}
secrets: inherit
1 change: 1 addition & 0 deletions fleetconfig-controller/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
# Ignore build and test binaries.
bin/
tmp/
3 changes: 2 additions & 1 deletion fleetconfig-controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ test-unit: manifests generate fmt vet envtest ## Run unit tests.
-coverprofile=$(COVER_DIR)/unit/cover.out \
$(shell go list ./... | grep -v '/test/e2e')

LABEL_FILTER ?= v1beta1
.PHONY: test-e2e
test-e2e: kind kubectl ginkgo support-bundle ## Run e2e tests in the top-level test directory.
@mkdir -p $(COVER_DIR)/e2e
Expand All @@ -125,7 +126,7 @@ test-e2e: kind kubectl ginkgo support-bundle ## Run e2e tests in the top-level t
$(GINKGO) run -vv \
--cover \
--coverpkg=./... \
--label-filter="fleetconfig" \
--label-filter="$(if $(LABEL_FILTER),$(LABEL_FILTER),v1beta1)" \
--output-dir=$(COVER_DIR)/e2e \
--timeout 20m \
./test/e2e/
Expand Down
4 changes: 2 additions & 2 deletions fleetconfig-controller/PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ resources:
path: github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1
version: v1beta1
webhooks:
defaulting: true
defaulting: false
validation: true
webhookVersion: v1
- api:
Expand All @@ -36,7 +36,7 @@ resources:
path: github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1
version: v1beta1
webhooks:
defaulting: true
defaulting: false
validation: true
webhookVersion: v1
version: "3"
32 changes: 30 additions & 2 deletions fleetconfig-controller/api/v1alpha1/fleetconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import (
"sort"
"time"

"open-cluster-management.io/ocm/pkg/operator/helpers/chart"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"open-cluster-management.io/ocm/pkg/operator/helpers/chart"

"github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/args"
)

// FleetConfigSpec defines the desired state of FleetConfig.
Expand Down Expand Up @@ -675,6 +676,33 @@ func (r *ResourceValues) String() string {
return ""
}

// GetRequests returns the resource requests.
func (r ResourceSpec) GetRequests() args.ResourceValues {
if r.Requests == nil {
return &ResourceValues{}
}
return r.Requests
}

// GetLimits returns the resource limits.
func (r ResourceSpec) GetLimits() args.ResourceValues {
if r.Limits == nil {
return &ResourceValues{}
}
return r.Limits
}

// GetQosClass returns the QoS class.
func (r ResourceSpec) GetQosClass() string {
return r.QosClass
}

// Ensure ResourceSpec implements args.ResourceSpec interface
var _ args.ResourceSpec = (*ResourceSpec)(nil)

// Ensure ResourceValues implements args.ResourceValues interface
var _ args.ResourceValues = (*ResourceValues)(nil)

// RegistrationAuth provides specifications for registration authentication.
type RegistrationAuth struct {
// The registration authentication driver to use.
Expand Down
157 changes: 157 additions & 0 deletions fleetconfig-controller/api/v1beta1/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package v1beta1

import (
"fmt"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/args"
)

// Kubeconfig is the configuration for a kubeconfig.
type Kubeconfig struct {
// A reference to an existing secret containing a kubeconfig.
// Must be provided for remote clusters.
// For same-cluster, must be provided unless InCluster is set to true.
// +optional
SecretReference *SecretReference `json:"secretReference,omitempty"`

// If set, the kubeconfig will be read from the cluster.
// Only applicable for same-cluster operations.
// Defaults to false.
// +optional
InCluster bool `json:"inCluster,omitempty"`

// The context to use in the kubeconfig file.
// +optional
Context string `json:"context,omitempty"`
}

// SecretReference describes how to retrieve a kubeconfig stored as a secret in the same namespace as the resource.
type SecretReference struct {
// The name of the secret.
// +required
Name string `json:"name"`

// The map key to access the kubeconfig. Defaults to 'kubeconfig'.
// +kubebuilder:default:="kubeconfig"
// +optional
KubeconfigKey string `json:"kubeconfigKey,omitempty"`
}

// ResourceSpec defines resource limits and requests for all managed clusters.
type ResourceSpec struct {
// The resource limits of all the containers managed by the Cluster Manager or Klusterlet operators.
// +optional
Limits *ResourceValues `json:"limits,omitempty"`

// The resource requests of all the containers managed by the Cluster Manager or Klusterlet operators.
// +optional
Requests *ResourceValues `json:"requests,omitempty"`

// The resource QoS class of all the containers managed by the Cluster Manager or Klusterlet operators.
// One of Default, BestEffort or ResourceRequirement.
// +kubebuilder:validation:Enum=Default;BestEffort;ResourceRequirement
// +kubebuilder:default:="Default"
// +optional
QosClass string `json:"qosClass,omitempty"`
}

// ResourceValues detail container resource constraints.
type ResourceValues struct {
// The number of CPU units to request, e.g., '800m'.
// +optional
CPU string `json:"cpu,omitempty"`

// The amount of memory to request, e.g., '8Gi'.
// +optional
Memory string `json:"memory,omitempty"`
}

// String returns a string representation of the resource values.
func (r *ResourceValues) String() string {
if r.CPU != "" && r.Memory != "" {
return fmt.Sprintf("cpu=%s,memory=%s", r.CPU, r.Memory)
} else if r.CPU != "" {
return fmt.Sprintf("cpu=%s", r.CPU)
} else if r.Memory != "" {
return fmt.Sprintf("memory=%s", r.Memory)
}
return ""
}

// GetRequests returns the resource requests.
func (r ResourceSpec) GetRequests() args.ResourceValues {
if r.Requests == nil {
return &ResourceValues{}
}
return r.Requests
}

// GetLimits returns the resource limits.
func (r ResourceSpec) GetLimits() args.ResourceValues {
if r.Limits == nil {
return &ResourceValues{}
}
return r.Limits
}

// GetQosClass returns the QoS class.
func (r ResourceSpec) GetQosClass() string {
return r.QosClass
}

// Ensure ResourceSpec implements args.ResourceSpec interface
var _ args.ResourceSpec = (*ResourceSpec)(nil)

// Ensure ResourceValues implements args.ResourceValues interface
var _ args.ResourceValues = (*ResourceValues)(nil)

// NewCondition returns a new v1beta1.Condition.
func NewCondition(msg, cType string, status, wantStatus metav1.ConditionStatus) Condition {
return Condition{
Condition: metav1.Condition{
Status: status,
Message: msg,
Reason: ReconcileSuccess,
Type: cType,
LastTransitionTime: metav1.Time{Time: time.Now()},
},
WantStatus: wantStatus,
}
}

// Condition describes the state of a FleetConfig.
type Condition struct {
metav1.Condition `json:",inline"`
WantStatus metav1.ConditionStatus `json:"wantStatus"`
}

// Equal returns true if the condition is identical to the supplied condition, ignoring the LastTransitionTime.
func (c Condition) Equal(other Condition) bool {
return c.Type == other.Type && c.Status == other.Status && c.WantStatus == other.WantStatus &&
c.Reason == other.Reason && c.Message == other.Message
}

// RegistrationAuth provides specifications for registration authentication.
type RegistrationAuth struct {
// The registration authentication driver to use.
// Options are:
// - csr: Use the default CSR-based registration authentication.
// - awsirsa: Use AWS IAM Role for Service Accounts (IRSA) registration authentication.
// The set of valid options is open for extension.
// +kubebuilder:validation:Enum=csr;awsirsa
// +kubebuilder:default:="csr"
// +optional
Driver string `json:"driver,omitempty"`

// The Hub cluster ARN for awsirsa registration authentication. Required when Type is awsirsa, otherwise ignored.
// +optional
HubClusterARN string `json:"hubClusterARN,omitempty"`

// List of AWS EKS ARN patterns so any EKS clusters with these patterns will be auto accepted to join with hub cluster.
// Example pattern: "arn:aws:eks:us-west-2:123456789013:cluster/.*"
// +optional
AutoApprovedARNPatterns []string `json:"autoApprovedARNPatterns,omitempty"`
}
Loading
Loading