mirror of
https://github.com/imjasonh/git-k8s
synced 2026-07-21 22:58:30 +00:00
Improve e2e smoke test with real git server and push controller exercise
Replace the stub smoke test (which only verified CRD creation) with a full end-to-end test that: - Deploys Gitea as an in-cluster git server (emptyDir, auto-install) - Initializes a test repository with an admin user and initial commit - Creates GitRepository, GitBranch, and GitPushTransaction CRDs - Verifies the push controller clones, pushes main to a new branch, and transitions the transaction to Succeeded - Verifies the controller updates the GitBranch CR's headCommit - Verifies the new branch actually exists on the git server Also fixes a bug in the push controller's resolveAuth: Secret data values are base64-encoded when accessed via the dynamic client, but were being used directly without decoding, causing authentication failures. https://claude.ai/code/session_01QfFzqKQUsxxBsiZUhuJ3pG
This commit is contained in:
parent
fe3fe95983
commit
aebe5c6c67
2 changed files with 204 additions and 6 deletions
193
.github/workflows/ci.yaml
vendored
193
.github/workflows/ci.yaml
vendored
|
|
@ -92,23 +92,208 @@ jobs:
|
||||||
kubectl get crd gitpushtransactions.git-k8s.imjasonh.com
|
kubectl get crd gitpushtransactions.git-k8s.imjasonh.com
|
||||||
kubectl get crd gitreposyncs.git-k8s.imjasonh.com
|
kubectl get crd gitreposyncs.git-k8s.imjasonh.com
|
||||||
|
|
||||||
- name: Smoke test - create a GitRepository
|
- name: Deploy Gitea git server
|
||||||
run: |
|
run: |
|
||||||
cat <<EOF | kubectl apply -f -
|
cat <<'EOF' | kubectl apply -f -
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
namespace: git-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gitea
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gitea
|
||||||
|
image: gitea/gitea:1.21
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
name: http
|
||||||
|
env:
|
||||||
|
- name: GITEA__database__DB_TYPE
|
||||||
|
value: sqlite3
|
||||||
|
- name: GITEA__security__INSTALL_LOCK
|
||||||
|
value: "true"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /api/v1/version
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
emptyDir: {}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
namespace: git-system
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: gitea
|
||||||
|
ports:
|
||||||
|
- port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
name: http
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Wait for Gitea
|
||||||
|
run: kubectl rollout status deployment/gitea -n git-system --timeout=120s
|
||||||
|
|
||||||
|
- name: Initialize Gitea
|
||||||
|
run: |
|
||||||
|
# Create admin user.
|
||||||
|
kubectl exec -n git-system deploy/gitea -- \
|
||||||
|
gitea admin user create \
|
||||||
|
--admin \
|
||||||
|
--username testadmin \
|
||||||
|
--password testpassword123 \
|
||||||
|
--email admin@test.local \
|
||||||
|
--must-change-password=false
|
||||||
|
|
||||||
|
# Create test repository with an initial commit on main.
|
||||||
|
kubectl exec -n git-system deploy/gitea -- \
|
||||||
|
curl -sf -X POST http://localhost:3000/api/v1/user/repos \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-u testadmin:testpassword123 \
|
||||||
|
-d '{"name":"test-repo","auto_init":true,"default_branch":"main"}'
|
||||||
|
echo ""
|
||||||
|
echo "Test repository created"
|
||||||
|
|
||||||
|
- name: Create Git credentials Secret
|
||||||
|
run: |
|
||||||
|
kubectl create secret generic gitea-creds \
|
||||||
|
--namespace=default \
|
||||||
|
--from-literal=username=testadmin \
|
||||||
|
--from-literal=password=testpassword123
|
||||||
|
|
||||||
|
- name: Create GitRepository
|
||||||
|
run: |
|
||||||
|
cat <<'EOF' | kubectl apply -f -
|
||||||
apiVersion: git-k8s.imjasonh.com/v1alpha1
|
apiVersion: git-k8s.imjasonh.com/v1alpha1
|
||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
metadata:
|
metadata:
|
||||||
name: test-repo
|
name: test-repo
|
||||||
namespace: default
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
url: https://github.com/example/test.git
|
url: http://gitea.git-system.svc.cluster.local:3000/testadmin/test-repo.git
|
||||||
|
defaultBranch: main
|
||||||
|
auth:
|
||||||
|
secretRef:
|
||||||
|
name: gitea-creds
|
||||||
EOF
|
EOF
|
||||||
kubectl get gitrepositories -A
|
kubectl get gitrepositories -A
|
||||||
|
|
||||||
|
- name: Create GitBranch
|
||||||
|
run: |
|
||||||
|
cat <<'EOF' | kubectl apply -f -
|
||||||
|
apiVersion: git-k8s.imjasonh.com/v1alpha1
|
||||||
|
kind: GitBranch
|
||||||
|
metadata:
|
||||||
|
name: test-repo-feature
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
repositoryRef: test-repo
|
||||||
|
branchName: feature-test
|
||||||
|
EOF
|
||||||
|
kubectl get gitbranches -A
|
||||||
|
|
||||||
|
- name: Push main to a new branch via GitPushTransaction
|
||||||
|
run: |
|
||||||
|
cat <<'EOF' | kubectl apply -f -
|
||||||
|
apiVersion: git-k8s.imjasonh.com/v1alpha1
|
||||||
|
kind: GitPushTransaction
|
||||||
|
metadata:
|
||||||
|
name: test-push
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
repositoryRef: test-repo
|
||||||
|
refSpecs:
|
||||||
|
- source: refs/heads/main
|
||||||
|
destination: refs/heads/feature-test
|
||||||
|
EOF
|
||||||
|
kubectl get gitpushtransactions -A
|
||||||
|
|
||||||
|
- name: Wait for push transaction to succeed
|
||||||
|
run: |
|
||||||
|
echo "Waiting for GitPushTransaction to complete..."
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
PHASE=$(kubectl get gitpushtransaction test-push -n default \
|
||||||
|
-o jsonpath='{.status.phase}' 2>/dev/null)
|
||||||
|
echo " attempt ${i}: phase=${PHASE:-Pending}"
|
||||||
|
if [ "$PHASE" = "Succeeded" ]; then
|
||||||
|
echo "Push transaction succeeded!"
|
||||||
|
break
|
||||||
|
elif [ "$PHASE" = "Failed" ]; then
|
||||||
|
echo "Push transaction FAILED!"
|
||||||
|
kubectl get gitpushtransaction test-push -n default -o yaml
|
||||||
|
kubectl logs -n git-system deploy/push-controller --tail=100
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
PHASE=$(kubectl get gitpushtransaction test-push -n default \
|
||||||
|
-o jsonpath='{.status.phase}')
|
||||||
|
if [ "$PHASE" != "Succeeded" ]; then
|
||||||
|
echo "Timed out waiting for push transaction (phase: ${PHASE:-empty})"
|
||||||
|
kubectl logs -n git-system deploy/push-controller --tail=100
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Verify push transaction result
|
||||||
|
run: |
|
||||||
|
echo "=== GitPushTransaction status ==="
|
||||||
|
kubectl get gitpushtransaction test-push -n default -o yaml
|
||||||
|
|
||||||
|
COMMIT=$(kubectl get gitpushtransaction test-push -n default \
|
||||||
|
-o jsonpath='{.status.resultCommit}')
|
||||||
|
echo "Result commit: $COMMIT"
|
||||||
|
if [ -z "$COMMIT" ]; then
|
||||||
|
echo "FAIL: resultCommit is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Verify GitBranch was updated by controller
|
||||||
|
run: |
|
||||||
|
echo "=== GitBranch status ==="
|
||||||
|
kubectl get gitbranch test-repo-feature -n default -o yaml
|
||||||
|
|
||||||
|
COMMIT=$(kubectl get gitbranch test-repo-feature -n default \
|
||||||
|
-o jsonpath='{.status.headCommit}')
|
||||||
|
echo "GitBranch headCommit: $COMMIT"
|
||||||
|
if [ -z "$COMMIT" ]; then
|
||||||
|
echo "FAIL: GitBranch headCommit was not updated by the controller"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Verify branch exists on git server
|
||||||
|
run: |
|
||||||
|
echo "Listing branches on Gitea..."
|
||||||
|
kubectl exec -n git-system deploy/gitea -- \
|
||||||
|
curl -sf http://localhost:3000/api/v1/repos/testadmin/test-repo/branches/feature-test \
|
||||||
|
-u testadmin:testpassword123
|
||||||
|
echo ""
|
||||||
|
echo "Branch feature-test verified on git server!"
|
||||||
|
|
||||||
- name: Collect diagnostics
|
- name: Collect diagnostics
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
uses: chainguard-dev/actions/kind-diag@main
|
uses: chainguard-dev/actions/kind-diag@main
|
||||||
with:
|
with:
|
||||||
artifact-name: kind-logs
|
artifact-name: kind-logs
|
||||||
cluster-resources: nodes,namespaces,crds
|
cluster-resources: nodes,namespaces,crds
|
||||||
namespace-resources: pods,deployments,services
|
namespace-resources: pods,deployments,services,gitrepositories,gitbranches,gitpushtransactions
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package push
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
|
|
@ -160,9 +161,21 @@ func (r *Reconciler) resolveAuth(ctx context.Context, namespace string, repo *gi
|
||||||
return nil, fmt.Errorf("reading secret data: found=%v err=%v", found, err)
|
return nil, fmt.Errorf("reading secret data: found=%v err=%v", found, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secret data values are base64-encoded in the API response when
|
||||||
|
// accessed via the dynamic client (unlike the typed client which
|
||||||
|
// decodes automatically into []byte fields).
|
||||||
|
username, err := base64.StdEncoding.DecodeString(data["username"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding username: %w", err)
|
||||||
|
}
|
||||||
|
password, err := base64.StdEncoding.DecodeString(data["password"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decoding password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &http.BasicAuth{
|
return &http.BasicAuth{
|
||||||
Username: data["username"],
|
Username: string(username),
|
||||||
Password: data["password"],
|
Password: string(password),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue