Skip to content

Commit 64a4d6f

Browse files
test(deployment): qualify explicit API-server token access with Core v0.4.0 (#50)
* test(deployment): qualify explicit API-server token access with Core v0.4.0 * fix(release): give tag workflow sole ownership of archive and SBOM assets * chore: adopt published Python v0.0.64 and Go source packager Consume the released generic Python owner changes and select the verified Go v0.0.54 source packager for publication. --------- Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com>
1 parent 5c7b2f3 commit 64a4d6f

8 files changed

Lines changed: 102 additions & 18 deletions

File tree

‎.github/workflows/releaser.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,20 @@ jobs:
3636
# on a bare runner, which has no macOS or cross-linux compilers. The
3737
# binary links Core's tree-sitter grammars (CGO), so releasing it needs
3838
# the toolchain this maintained image supplies — see .goreleaser.yaml.
39+
- name: Install Syft for archive SBOMs
40+
id: syft
41+
uses: anchore/sbom-action/download-syft@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2
42+
with:
43+
syft-version: v1.44.0
3944
- name: Run CGO-capable GoReleaser
4045
run: >-
4146
docker run --rm
4247
-e GITHUB_TOKEN
4348
-v "$GITHUB_WORKSPACE:/workspace"
49+
-v "$SYFT_PATH:/usr/local/bin/syft:ro"
4450
-w /workspace
4551
ghcr.io/goreleaser/goreleaser-cross:v1.27.1-1
4652
release --clean
4753
env:
4854
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
55+
SYFT_PATH: ${{ steps.syft.outputs.cmd }}

‎.goreleaser.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
version: 2
22

3+
sboms:
4+
- id: archives
5+
artifacts: archive
6+
documents:
7+
- "${artifact}.sbom.json"
8+
disable: false
9+
310
builds:
411
# ARCHITECTURE: Core's authoritative source inspection uses real
512
# tree-sitter grammars, which are CGO. Agent releases therefore require

‎agent.codefly.yaml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ publisher: codefly.dev
22
kind: codefly:service
33
name: python-fastapi
44
version: 0.0.102
5+
release:
6+
owner: workflow
7+
workflow: releaser.yml
58
source:
69
directory: .
7-
agent: codefly.dev/go:0.0.51
10+
agent: codefly.dev/go:0.0.54

‎builder.go‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,7 @@ type Parameters struct {
249249
// AutomountServiceAccountToken projects the ServiceAccount token into the
250250
// pod. It stays false because the identity path is the metadata server or a
251251
// webhook-projected token and the app does not call the API server. Setting
252-
// it true currently renders a manifest core's own manifest conformance
253-
// rejects (codefly-dev/core#602), so the field is the seam, not yet a
254-
// working switch.
252+
// it true requires the pod annotation codefly.dev/api-server-access: required.
255253
AutomountServiceAccountToken bool
256254
}
257255

‎deployment_test.go‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,7 @@ func TestDeploymentRendersOptionalConfigMount(t *testing.T) {
201201
require.Equal(t, false, *pod.Spec.Containers[0].VolumeMounts[1].ReadOnly)
202202
}
203203

204-
// TestDeploymentAutomountServiceAccountTokenRejectsProjection pins the reason
205-
// the parameter defaults to false and cannot yet be flipped: core's manifest
206-
// conformance rejects a projected token for every workload, so a service that
207-
// needs to call the API server is blocked in core, not here.
208-
func TestDeploymentAutomountServiceAccountTokenRejectsProjection(t *testing.T) {
204+
func TestDeploymentAutomountServiceAccountTokenRejectsUndeclaredProjection(t *testing.T) {
209205
_, destination := renderDeployment(t, Parameters{}, nil)
210206

211207
path := filepath.Join(destination, "base", "deployment.yaml")
@@ -219,9 +215,20 @@ func TestDeploymentAutomountServiceAccountTokenRejectsProjection(t *testing.T) {
219215
builderv0.KubernetesOutputProfile_KUBERNETES_OUTPUT_PROFILE_EPHEMERAL_LOCAL_APPLY_V1, false, "", "")
220216

221217
require.Contains(t, validation.GetViolations(),
222-
"Deployment/example-service must set automountServiceAccountToken: false",
223-
"core no longer rejects a projected token: codefly-dev/core#602 is resolved, so "+
224-
"AutomountServiceAccountToken is now usable and this test should assert the render instead")
218+
"Deployment/example-service must set automountServiceAccountToken: false, or declare codefly.dev/api-server-access: required")
219+
}
220+
221+
func TestDeploymentAutomountServiceAccountTokenRendersDeclaredProjection(t *testing.T) {
222+
pod, destination := renderDeployment(t, Parameters{AutomountServiceAccountToken: true}, &services.PodTemplateOverlay{
223+
PodAnnotations: map[string]string{services.AnnotationAPIServerAccess: services.APIServerAccessRequired},
224+
})
225+
require.NotNil(t, pod.Spec.AutomountServiceAccountToken)
226+
require.True(t, *pod.Spec.AutomountServiceAccountToken)
227+
require.Equal(t, services.APIServerAccessRequired, pod.Metadata.Annotations[services.AnnotationAPIServerAccess])
228+
229+
validation := services.ValidateKubernetesManifestTree(t.Context(), destination, "test", "codefly-test",
230+
builderv0.KubernetesOutputProfile_KUBERNETES_OUTPUT_PROFILE_EPHEMERAL_LOCAL_APPLY_V1, false, "", "")
231+
require.Empty(t, validation.GetViolations())
225232
}
226233

227234
// TestPreparePodOverlayRejectsScratchCollision covers the mounts core's own

‎go.mod‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/codefly-dev/service-python-fastapi
33
go 1.27.0
44

55
require (
6-
github.com/codefly-dev/core v0.3.41
7-
github.com/codefly-dev/service-python v0.0.62
6+
github.com/codefly-dev/core v0.4.0
7+
github.com/codefly-dev/service-python v0.0.64
88
github.com/stretchr/testify v1.12.1
99
google.golang.org/grpc v1.83.2
1010
gopkg.in/yaml.v3 v3.0.1

‎go.sum‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJ
4343
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
4444
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
4545
github.com/cloudflare/circl v1.6.5/go.mod h1:h5LNyxAc5nTue9DS5jT+48en2PSDYt3zdGnz5OstK6c=
46-
github.com/codefly-dev/core v0.3.41 h1:Etae1jOIWEl524U7tnCT3TaDPUtU5WGsf1XhlFALvt8=
47-
github.com/codefly-dev/core v0.3.41/go.mod h1:uG0HQy5iWnkuWUPa1LbbiPfEgzuxi1SzEKwcFPC6eoA=
46+
github.com/codefly-dev/core v0.4.0 h1:xMW9pV+d0mEvxTs4Jqrs5hr4OCuIy1Fvz0aE3/a5LIM=
47+
github.com/codefly-dev/core v0.4.0/go.mod h1:uG0HQy5iWnkuWUPa1LbbiPfEgzuxi1SzEKwcFPC6eoA=
4848
github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ=
4949
github.com/codefly-dev/gortk v0.2.0/go.mod h1:dDWUMFgAP063OCGhTEpV7FFUj9+zTcxxO/nV80VKEwg=
50-
github.com/codefly-dev/service-python v0.0.62 h1:quru4JzJi8v5nhiYTzXjl2SswXIasyeYq/iaM6TvN4k=
51-
github.com/codefly-dev/service-python v0.0.62/go.mod h1:LEJP0KA7BRALgQU8YLBwwVxXls5WwQLQ8esbMDG6IOY=
50+
github.com/codefly-dev/service-python v0.0.64 h1:6C0GIu3+c/P3FShvk/lJgGlMkUSIuXxlTCo3RGMHntk=
51+
github.com/codefly-dev/service-python v0.0.64/go.mod h1:s9Se4j0mfGmGo/HPLMtcwDfL31G8c++I8/sDycxMHpI=
5252
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
5353
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
5454
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=

‎release_test.go‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
9+
"gopkg.in/yaml.v3"
10+
)
11+
12+
func TestReleaseDeclaresOnePublisherAndArchiveSBOMs(t *testing.T) {
13+
read := func(path string, target any) {
14+
t.Helper()
15+
payload, err := os.ReadFile(path)
16+
if err != nil {
17+
t.Fatal(err)
18+
}
19+
if err := yaml.Unmarshal(payload, target); err != nil {
20+
t.Fatal(err)
21+
}
22+
}
23+
var manifest struct {
24+
Release struct{ Owner, Workflow string }
25+
}
26+
read("agent.codefly.yaml", &manifest)
27+
if manifest.Release.Owner != "workflow" || manifest.Release.Workflow != "releaser.yml" {
28+
t.Fatal("the tag workflow must be the sole artifact publisher")
29+
}
30+
var config struct {
31+
SBOMs []struct {
32+
Artifacts string
33+
Documents []string
34+
Disable bool
35+
} `yaml:"sboms"`
36+
}
37+
read(".goreleaser.yaml", &config)
38+
if len(config.SBOMs) != 1 || config.SBOMs[0].Artifacts != "archive" || config.SBOMs[0].Disable || len(config.SBOMs[0].Documents) != 1 || config.SBOMs[0].Documents[0] != "${artifact}.sbom.json" {
39+
t.Fatal("each published archive must carry its canonical SBOM")
40+
}
41+
var workflow struct {
42+
Jobs map[string]struct {
43+
Steps []struct {
44+
ID, Uses, Run string
45+
Env map[string]string
46+
}
47+
}
48+
}
49+
read(filepath.Join(".github", "workflows", manifest.Release.Workflow), &workflow)
50+
installed, mounted := false, false
51+
for _, step := range workflow.Jobs["release"].Steps {
52+
if step.ID == "syft" && strings.HasPrefix(step.Uses, "anchore/sbom-action/download-syft@") {
53+
installed = true
54+
}
55+
if strings.Contains(step.Run, `-v "$SYFT_PATH:/usr/local/bin/syft:ro"`) && step.Env["SYFT_PATH"] == "${{ steps.syft.outputs.cmd }}" {
56+
mounted = true
57+
}
58+
}
59+
if !installed || !mounted {
60+
t.Fatal("the canonical cross build must have Syft installed and mounted")
61+
}
62+
}

0 commit comments

Comments
 (0)