Skip to content

Commit 640d6de

Browse files
authored
Merge pull request #568 from vshn/develop
🔀 Merge develop into master (Release)
2 parents a4578cd + 5d3a69d commit 640d6de

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3892
-202
lines changed

.github/workflows/merge.yml

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,92 @@ jobs:
164164
env:
165165
GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }}
166166
steps:
167+
- name: Merge PR in appcat repo
168+
run: |
169+
echo "✅ Merging appcat PR: https://github.com/$APPCAT_REPO/pull/${{ needs.check-conditions.outputs.pr-number }}"
170+
gh pr merge -R "$APPCAT_REPO" ${{ needs.check-conditions.outputs.pr-number }} --merge --delete-branch
171+
env:
172+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
173+
174+
- name: Get merge commit SHA from PR
175+
id: merge-commit
176+
run: |
177+
# Wait for merge to be reflected in API
178+
sleep 2
179+
180+
# Get the merge commit SHA directly from the PR
181+
MERGE_COMMIT_SHA=$(gh pr view ${{ needs.check-conditions.outputs.pr-number }} \
182+
-R "$APPCAT_REPO" \
183+
--json mergeCommit \
184+
--jq '.mergeCommit.oid')
185+
186+
# Verify we got the commit that contains our PR's changes
187+
LAST_PR_COMMIT="${{ needs.check-conditions.outputs.last-sha }}"
188+
189+
# Check if the merge commit has our last PR commit as a parent
190+
PARENTS=$(gh api repos/$APPCAT_REPO/commits/$MERGE_COMMIT_SHA --jq '[.parents[].sha] | join(" ")')
191+
192+
if ! echo "$PARENTS" | grep -q "$LAST_PR_COMMIT"; then
193+
echo "❌ Merge commit $MERGE_COMMIT_SHA does not contain PR commit $LAST_PR_COMMIT"
194+
echo "Parents: $PARENTS"
195+
exit 1
196+
fi
197+
198+
echo "🔧 Merge commit SHA: $MERGE_COMMIT_SHA (verified)"
199+
echo "sha=$MERGE_COMMIT_SHA" >> "$GITHUB_OUTPUT"
200+
env:
201+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
202+
203+
- name: Checkout appcat repository at merge commit
204+
uses: actions/checkout@v4
205+
with:
206+
repository: ${{ env.APPCAT_REPO }}
207+
ref: ${{ steps.merge-commit.outputs.sha }}
208+
token: ${{ secrets.GITHUB_TOKEN }}
209+
210+
- name: Determine Go version from go.mod
211+
run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV
212+
213+
- uses: actions/setup-go@v5
214+
with:
215+
go-version: ${{ env.GO_VERSION }}
216+
217+
- name: Set up QEMU
218+
uses: docker/setup-qemu-action@v3
219+
220+
- name: Set up Docker Buildx
221+
uses: docker/setup-buildx-action@v3
222+
223+
- name: Login to GHCR
224+
uses: docker/login-action@v3
225+
with:
226+
registry: ghcr.io
227+
username: ${{ github.repository_owner }}
228+
password: ${{ secrets.GITHUB_TOKEN }}
229+
230+
- name: Build and push Docker image with merge commit SHA
231+
run: |
232+
echo "🐳 Building and pushing image with tag: ${{ steps.merge-commit.outputs.sha }}"
233+
make docker-push -e IMG_TAG=${{ steps.merge-commit.outputs.sha }} -e APP_NAME=${{ env.APP_NAME }}
234+
235+
- name: Build and push function package with merge commit SHA
236+
run: |
237+
echo "📦 Building and pushing function package with tag: ${{ steps.merge-commit.outputs.sha }}"
238+
make package-push -e IMG_TAG=${{ steps.merge-commit.outputs.sha }} -e APP_NAME=${{ env.APP_NAME }}
239+
167240
- name: Clone and patch component repo
168241
run: |
169242
COMPONENT_BRANCH="${{ needs.check-conditions.outputs.comp-feature-branch }}"
170-
APPCAT_FR_COMMIT_SHA="${{ needs.check-conditions.outputs.last-sha }}"
171-
echo "🔧 Using commit: $APPCAT_FR_COMMIT_SHA"
243+
APPCAT_MERGE_COMMIT_SHA="${{ steps.merge-commit.outputs.sha }}"
244+
echo "🔧 Using merge commit: $APPCAT_MERGE_COMMIT_SHA"
172245
echo "🔧 Cloning branch: $COMPONENT_BRANCH"
173246
174247
git clone https://x-access-token:$GH_TOKEN@github.com/"$COMPONENT_REPO".git
175248
cd component-appcat
176249
git checkout "$COMPONENT_BRANCH"
177250
178251
echo "🔧 Patching class/defaults.yml..."
179-
yq e ".parameters.appcat.images.appcat.tag = \"$APPCAT_FR_COMMIT_SHA\"" class/defaults.yml \
252+
yq e ".parameters.appcat.images.appcat.tag = \"$APPCAT_MERGE_COMMIT_SHA\"" class/defaults.yml \
180253
| diff -B class/defaults.yml - \
181254
| patch class/defaults.yml - || echo "✅ No patch needed"
182255
@@ -189,7 +262,7 @@ jobs:
189262
if git diff --cached --quiet; then
190263
echo "✅ No changes to commit"
191264
else
192-
git commit -m "Auto update from appcat PR https://github.com/${{ env.APPCAT_REPO }}/pull/${{ needs.check-conditions.outputs.pr-number }}, dependency $APPCAT_FR_COMMIT_SHA"
265+
git commit -m "Auto update from appcat PR https://github.com/${{ env.APPCAT_REPO }}/pull/${{ needs.check-conditions.outputs.pr-number }}, dependency $APPCAT_MERGE_COMMIT_SHA"
193266
git push origin "$COMPONENT_BRANCH"
194267
fi
195268
@@ -210,11 +283,6 @@ jobs:
210283
echo "✅ Merging component PR: ${{ needs.check-conditions.outputs.comp-url }}"
211284
gh pr merge -R "$COMPONENT_REPO" ${{ needs.check-conditions.outputs.comp-pr-number }} --merge --delete-branch
212285
213-
- name: Merge PR in appcat repo
214-
run: |
215-
echo "✅ Merging appcat PR: https://github.com/$APPCAT_REPO/pull/${{ needs.check-conditions.outputs.pr-number }}"
216-
gh pr merge -R "$APPCAT_REPO" ${{ needs.check-conditions.outputs.pr-number }} --merge --delete-branch
217-
218286
merge-hotfix:
219287
needs: check-conditions
220288
if: needs.check-conditions.outputs.base-branch == 'master' && needs.check-conditions.outputs.is-hotfix == 'true'

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SHELL := /bin/bash
1+
SHELL := /usr/bin/env bash
22

33
PROJECT_ROOT_DIR = .
44
PROJECT_NAME ?= appcat

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ The controller manages and achieves the following:
141141
| --------------------- | ----------------------------------------------- | -------------------------------------------- |
142142
| `XVSHNPostgreSQL` | Deletion Protection Support | Manages finalizers before and after deletion |
143143
| `validation webhooks` | Provide validation webhooks for AppCat services | See Goal |
144+
| `crossplane metrics` | Provides crossplane resource metrics | See [here](pkg/controller/crossplane_metrics/README.md) for more information |
144145

145146
### Local Webhook debugging
146147

cmd/controller.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
package cmd
22

33
import (
4+
"encoding/json"
45
"fmt"
56

67
"github.com/go-logr/logr"
78
"github.com/spf13/cobra"
89
"github.com/spf13/viper"
910
"github.com/vshn/appcat/v4/pkg"
1011
"github.com/vshn/appcat/v4/pkg/controller/billing"
12+
"github.com/vshn/appcat/v4/pkg/controller/crossplane_metrics"
1113
"github.com/vshn/appcat/v4/pkg/controller/events"
1214
"github.com/vshn/appcat/v4/pkg/controller/webhooks"
1315
"github.com/vshn/appcat/v4/pkg/odoo"
1416
"k8s.io/apimachinery/pkg/runtime"
17+
"k8s.io/client-go/dynamic"
1518
ctrl "sigs.k8s.io/controller-runtime"
1619
"sigs.k8s.io/controller-runtime/pkg/healthz"
1720
"sigs.k8s.io/controller-runtime/pkg/manager"
21+
"sigs.k8s.io/controller-runtime/pkg/metrics"
1822
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
1923
"sigs.k8s.io/controller-runtime/pkg/webhook"
2024
)
@@ -29,6 +33,7 @@ type controller struct {
2933
enableQuotas bool
3034
enableEventForwarding bool
3135
enableBilling bool
36+
enableCrossplaneMetrics bool
3237
certDir string
3338
}
3439

@@ -55,6 +60,7 @@ func init() {
5560
ControllerCMD.Flags().BoolVar(&c.enableQuotas, "quotas", false, "Enable the quota webhooks, is only active if webhooks is also true")
5661
ControllerCMD.Flags().BoolVar(&c.enableEventForwarding, "event-forwarding", true, "Disable event-forwarding")
5762
ControllerCMD.Flags().BoolVar(&c.enableBilling, "billing", true, "Disable billing")
63+
ControllerCMD.Flags().BoolVar(&c.enableCrossplaneMetrics, "crossplane-metrics", false, "Enable Crossplane resource metrics collector")
5864
viper.AutomaticEnv()
5965
if !viper.IsSet("PLANS_NAMESPACE") {
6066
viper.Set("PLANS_NAMESPACE", "syn-appcat")
@@ -132,6 +138,43 @@ func (c *controller) executeController(cmd *cobra.Command, _ []string) error {
132138
}
133139
}
134140

141+
if c.enableCrossplaneMetrics {
142+
// Parse configuration from environment variables
143+
labelMapping := make(map[string]string)
144+
if labelMappingJSON := viper.GetString("CROSSPLANE_LABEL_MAPPING"); labelMappingJSON != "" {
145+
if err := json.Unmarshal([]byte(labelMappingJSON), &labelMapping); err != nil {
146+
return fmt.Errorf("failed to parse CROSSPLANE_LABEL_MAPPING: %w", err)
147+
}
148+
} else {
149+
return fmt.Errorf("CROSSPLANE_LABEL_MAPPING environment variable is required when crossplane-metrics is enabled")
150+
}
151+
152+
if len(labelMapping) == 0 {
153+
return fmt.Errorf("CROSSPLANE_LABEL_MAPPING cannot be empty when crossplane-metrics is enabled")
154+
}
155+
156+
// Parse extra resources (optional)
157+
extraResources := make(map[string][]string)
158+
if extraResourcesStr := viper.GetString("CROSSPLANE_EXTRA_RESOURCES"); extraResourcesStr != "" {
159+
if err := json.Unmarshal([]byte(extraResourcesStr), &extraResources); err != nil {
160+
return fmt.Errorf("failed to parse CROSSPLANE_EXTRA_RESOURCES: %w", err)
161+
}
162+
}
163+
164+
// Create dynamic client for Crossplane resources
165+
dynamicClient, err := dynamic.NewForConfig(mgr.GetConfig())
166+
if err != nil {
167+
return fmt.Errorf("failed to create dynamic client: %w", err)
168+
}
169+
170+
// Create and register the collector
171+
collector := crossplane_metrics.NewMetricsCollector(dynamicClient, labelMapping, extraResources, log)
172+
if err := metrics.Registry.Register(collector); err != nil {
173+
return fmt.Errorf("failed to register Crossplane metrics collector: %w", err)
174+
}
175+
log.Info("Crossplane metrics collector registered successfully")
176+
}
177+
135178
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
136179
return err
137180
}
@@ -218,5 +261,5 @@ func setupWebhooks(mgr manager.Manager, withQuota bool, withAppcatWebhooks bool,
218261
if err != nil {
219262
return err
220263
}
221-
return webhooks.SetupPVCDeletionProtectionHandlerWithManager(mgr)
264+
return webhooks.SetupUnmanagedProtectionWebhookWithManager(mgr)
222265
}

cmd/maintenance.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ var (
5353
type Maintenance interface {
5454
DoMaintenance(ctx context.Context) error
5555
ReleaseLatest(ctx context.Context, enabled bool, kubeClient client.Client, minAge time.Duration) error
56+
// RunBackup runs a pre-maintenance backup if supported
57+
RunBackup(ctx context.Context) error
5658
}
5759

5860
type service enumflag.Flag
@@ -163,9 +165,16 @@ func (c *controller) runMaintenance(cmd *cobra.Command, _ []string) error {
163165
}
164166

165167
if err = errors.Join(
166-
// We do the release first and then the maintenance
167-
// This is to avoid deadlocks, where a maintenance might be broken
168-
// in a version and the fix is in the next version.
168+
// Run backup before any changes, then release, then maintenance
169+
func() error {
170+
log.Info("Running pre-maintenance backup")
171+
if err := m.RunBackup(ctx); err != nil {
172+
log.Error(err, "Pre-maintenance backup failed")
173+
return fmt.Errorf("pre-maintenance backup failed: %w", err)
174+
}
175+
log.Info("Pre-maintenance backup completed successfully")
176+
return nil
177+
}(),
169178
func() error {
170179
enabled, err := strconv.ParseBool(viper.GetString("RELEASE_MANAGEMENT_ENABLED"))
171180
if err != nil {

config/controller/cluster-role.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,18 @@ rules:
126126
- xvshnpostgresqls/status
127127
- xvshnredis
128128
- xvshnredis/status
129+
- xobjectbuckets
129130
verbs:
130131
- get
131132
- list
132133
- patch
133134
- update
134135
- watch
136+
- apiGroups:
137+
- apiextensions.crossplane.io
138+
resources:
139+
- compositeresourcedefinitions
140+
verbs:
141+
- get
142+
- list
143+
- watch

config/controller/service.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: appcat-controller
5+
namespace: appcat
6+
labels:
7+
appcat-controller: appcat-controller
8+
spec:
9+
ports:
10+
- port: 8080
11+
name: metrics
12+
protocol: TCP
13+
targetPort: 8080
14+
selector:
15+
appcat-controller: appcat-controller
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: monitoring.coreos.com/v1
2+
kind: ServiceMonitor
3+
metadata:
4+
name: appcat
5+
namespace: appcat
6+
spec:
7+
endpoints:
8+
- path: /metrics
9+
port: metrics
10+
selector:
11+
matchLabels:
12+
appcat-controller: appcat-controller

config/controller/webhooks.yaml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -141,25 +141,6 @@ webhooks:
141141
resources:
142142
- vshnpostgresqls
143143
sideEffects: None
144-
- admissionReviewVersions:
145-
- v1
146-
clientConfig:
147-
service:
148-
name: webhook-service
149-
namespace: system
150-
path: /validate--v1-persistentvolumeclaim
151-
failurePolicy: Fail
152-
name: pvc.vshn.appcat.vshn.io
153-
rules:
154-
- apiGroups:
155-
- ""
156-
apiVersions:
157-
- v1
158-
operations:
159-
- DELETE
160-
resources:
161-
- persistentvolumeclaims
162-
sideEffects: None
163144
- admissionReviewVersions:
164145
- v1
165146
clientConfig:

pkg/comp-functions/functions/vshnpostgres/billing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func AddBilling(ctx context.Context, comp *v1.VSHNPostgreSQL, svc *runtime.Servi
2424
return prometheusResult
2525
}
2626

27-
if comp.GetLabels()[runtime.OwnerCompositeAnnotation] != "" {
27+
if comp.GetLabels()[runtime.OwnerCompositeLabel] != "" {
2828
return runtime.NewNormalResult(fmt.Sprintf("Skipping Billing as composite is a subservice %s", comp.GetName()))
2929
}
3030

0 commit comments

Comments
 (0)