Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tekton Workload Identity #102

Merged
merged 31 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d884cb5
add service account and todo
Richard87 Dec 6, 2023
aa98390
move test to Workflow, add linting
Richard87 Dec 6, 2023
46f0c95
Rename workload
Richard87 Dec 6, 2023
eef7285
linting
Richard87 Dec 6, 2023
cd9bf36
validate labels and annotations
Richard87 Dec 6, 2023
38f930d
validate label and add owner reference
Richard87 Dec 7, 2023
a2fe8dd
Validate label and annotations in task_validations
Richard87 Dec 7, 2023
5d9157d
skip containers
Richard87 Dec 7, 2023
98903c2
skip containers
Richard87 Dec 7, 2023
3caf3e3
Use same pipeline run pattern as Radix Operator that is easier to mock
Richard87 Dec 12, 2023
39d186e
cleanup
Richard87 Dec 12, 2023
9d69ce7
cleanup
Richard87 Dec 13, 2023
497e683
Run all tests, fix panics
Richard87 Dec 13, 2023
ea99103
simplify and rename validate skip containers logic
Richard87 Dec 13, 2023
96b9546
test unknown steps fails
Richard87 Dec 13, 2023
b706a27
fix tests
Richard87 Dec 13, 2023
112775e
fix pr comments
Richard87 Dec 13, 2023
15099ce
use options pattern for Waiter
Richard87 Dec 13, 2023
f544afd
replace wait condition to completed instead of True
Richard87 Dec 14, 2023
a2f2013
follow client naming conventions
Richard87 Dec 14, 2023
bb0024f
code style, most relevant argument first
Richard87 Dec 14, 2023
4e43a12
added error handling to pipelinerun waiter
Richard87 Dec 14, 2023
a1febd6
cleanup wait
Richard87 Dec 14, 2023
acb586d
cleanup sanitazion
Richard87 Dec 14, 2023
e10206c
cleanup sanitazion
Richard87 Dec 14, 2023
c1d4253
more tests
Richard87 Dec 14, 2023
860011b
inline error handler
Richard87 Dec 14, 2023
563d567
remove rendundant information
Richard87 Dec 14, 2023
a05bd03
remove redundant information
Richard87 Dec 14, 2023
a3a2c7b
fix bug in waiter
Richard87 Dec 14, 2023
2cd827d
remove deprecated erros util package
Richard87 Dec 14, 2023
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
44 changes: 44 additions & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Pull-Request Testing
on:
pull_request:
workflow_dispatch:

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Install GolangCI Lint
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2

- name: golangci-lint
run: golangci-lint run --timeout=30m --max-same-issues=0 --out-format=github-actions

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Run Tests
run: go test -cover `go list ./... | grep -v 'pkg/client'`

build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build docker image
run: docker build .
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ RUN go mod download
COPY ./pkg ./pkg
COPY ./main.go ./

# Run test
RUN go test -cover `go list ./... | grep -v 'pkg/client'`

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -a -installsuffix cgo -o ./rootfs/radix-tekton
RUN adduser -D -g '' -u 1000 radix-user
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ echo:


.PHONY: test
test:
test:
go test -cover `go list ./... | grep -v 'pkg/client'`

.PHONY: build
Expand All @@ -63,6 +63,7 @@ deploy: build
.PHONY: mocks
mocks:
mockgen -source ./pkg/models/env/env.go -destination ./pkg/models/env/env_mock.go -package env
mockgen -source ./pkg/internal/wait/pipelinerun.go -destination ./pkg/internal/wait/pipelinerun_mock.go -package wait

.PHONY: staticcheck
staticcheck:
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.21
toolchain go1.21.0

require (
github.com/equinor/radix-common v1.5.0
github.com/equinor/radix-operator v1.45.7
github.com/equinor/radix-common v1.7.0
github.com/equinor/radix-operator v1.46.0
github.com/go-git/go-git/v5 v5.9.0
github.com/golang/mock v1.6.0
github.com/pkg/errors v0.9.1
Expand Down
12 changes: 4 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,10 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/equinor/radix-common v1.5.0 h1:z5hQHlKG2x16/NnV4b9ynf9n5ZageYUewE4MANdA96Y=
github.com/equinor/radix-common v1.5.0/go.mod h1:UZ69U56VFtTxABi5JjGdaqn9Df5ilfTTqzUQ0riofVM=
github.com/equinor/radix-operator v1.45.2 h1:4ER9XPu5LN2Sn1BeYoXtHeLOn1Ui5OhigFF3LyUiQNg=
github.com/equinor/radix-operator v1.45.2/go.mod h1:XQdwUORrzp9nnWYmm6nhQn2Qm8iy+UIdlAVWBvBUMuo=
github.com/equinor/radix-operator v1.45.7 h1:NQOstC2+lVm4A9ty454UKvBdBa4OXywoW3nCw2omzHk=
github.com/equinor/radix-operator v1.45.7/go.mod h1:XQdwUORrzp9nnWYmm6nhQn2Qm8iy+UIdlAVWBvBUMuo=
github.com/equinor/radix-operator v1.99.1000-0.20231127082416-9f69c2f892f5 h1:r3YNNDftMtc5BdZ9rHfDzazQyfGOd/takbZVE5Dw4/E=
github.com/equinor/radix-operator v1.99.1000-0.20231127082416-9f69c2f892f5/go.mod h1:XQdwUORrzp9nnWYmm6nhQn2Qm8iy+UIdlAVWBvBUMuo=
github.com/equinor/radix-common v1.7.0 h1:DJHoBZTNzqEHw9faIWrSPGhPKEew/HBWc9MLCek6qgI=
github.com/equinor/radix-common v1.7.0/go.mod h1:9hHvudaiqmoIjCqKlsW14jMj8qU/b/wMXUwkffd9MUw=
github.com/equinor/radix-operator v1.46.0 h1:UF8WZsRLAFmMMYcaUvx/t9lvjnJXdPp0CCbRZAKCsOQ=
github.com/equinor/radix-operator v1.46.0/go.mod h1:ygJYuO8paAlRPEvCqwAqXCRGe7WvRrzX1yAjULAH2DA=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
Expand Down
128 changes: 128 additions & 0 deletions pkg/internal/wait/pipelinerun.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package wait

import (
"errors"
"fmt"
"sort"
"time"

"github.com/equinor/radix-tekton/pkg/models/env"
log "github.com/sirupsen/logrus"
pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
tektonclient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
tektonInformerFactory "github.com/tektoncd/pipeline/pkg/client/informers/externalversions"
"k8s.io/client-go/tools/cache"
knativeApis "knative.dev/pkg/apis"
knative "knative.dev/pkg/apis/duck/v1"
)

type PipelineRunsCompletionWaiter interface {
Wait(pipelineRuns map[string]*pipelinev1.PipelineRun, env env.Env) error
}

func NewPipelineRunsCompletionWaiter(tektonClient tektonclient.Interface) PipelineRunsCompletionWaiter {
return PipelineRunsCompletionWaiterFunc(func(pipelineRuns map[string]*pipelinev1.PipelineRun, env env.Env) error {
return waitForCompletionOf(pipelineRuns, tektonClient, env)
})
}

type PipelineRunsCompletionWaiterFunc func(pipelineRuns map[string]*pipelinev1.PipelineRun, env env.Env) error

func (f PipelineRunsCompletionWaiterFunc) Wait(pipelineRuns map[string]*pipelinev1.PipelineRun, env env.Env) error {
return f(pipelineRuns, env)
}

// WaitForCompletionOf Will wait for job to complete
func waitForCompletionOf(pipelineRuns map[string]*pipelinev1.PipelineRun, tektonClient tektonclient.Interface, env env.Env) error {
stop := make(chan struct{})
defer close(stop)

if len(pipelineRuns) == 0 {
return nil
}

errChan := make(chan error)

kubeInformerFactory := tektonInformerFactory.NewSharedInformerFactoryWithOptions(tektonClient, time.Second*5, tektonInformerFactory.WithNamespace(env.GetAppNamespace()))
genericInformer, err := kubeInformerFactory.ForResource(pipelinev1.SchemeGroupVersion.WithResource("pipelineruns"))
if err != nil {
return fmt.Errorf("waitForCompletionOf failed to create informer: %w", err)
}
informer := genericInformer.Informer()
_, err = informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
UpdateFunc: func(old, cur interface{}) {
run, success := cur.(*pipelinev1.PipelineRun)
if !success {
errChan <- errors.New("updatefunc conversion failed")
return
Richard87 marked this conversation as resolved.
Show resolved Hide resolved
}
pipelineRun, ok := pipelineRuns[run.GetName()]
if !ok {
return
}
if pipelineRun.GetName() == run.GetName() && pipelineRun.GetNamespace() == run.GetNamespace() && run.Status.PipelineRunStatusFields.CompletionTime != nil {
conditions := sortByTimestampDesc(run.Status.Conditions)
if len(conditions) == 0 {
return
}
delete(pipelineRuns, run.GetName())
lastCondition := conditions[0]

switch {
case lastCondition.Reason == pipelinev1.PipelineRunReasonCompleted.String():
log.Infof("pipelineRun completed: %s", lastCondition.Message)
case lastCondition.Reason == pipelinev1.PipelineRunReasonFailed.String():
errChan <- fmt.Errorf("PipelineRun failed: %s", lastCondition.Message)
return
default:
log.Infof("pipelineRun status %s: %s", lastCondition.Reason, lastCondition.Message)
}
if len(pipelineRuns) == 0 {
errChan <- nil
}
} else {
log.Debugf("Ongoing - PipelineRun has not completed yet")
}
},
DeleteFunc: func(old interface{}) {
run, success := old.(*pipelinev1.PipelineRun)
if !success {
errChan <- errors.New("deletefunc conversion failed")
return
}
pipelineRun, ok := pipelineRuns[run.GetName()]
if !ok {
return
}
if pipelineRun.GetNamespace() == run.GetNamespace() {
delete(pipelineRuns, run.GetName())
errChan <- errors.New("pipelineRun failed - Job deleted")
}
},
})
if err != nil {
return fmt.Errorf("waitForCompletionOf failed to create event handler: %w", err)
}

go informer.Run(stop)
if !cache.WaitForCacheSync(stop, informer.HasSynced) {
errChan <- fmt.Errorf("timed out waiting for caches to sync")
}

err = <-errChan
if err != nil {
return fmt.Errorf("waitForCompletionOf failed during wait: %w", err)
}
return nil
}

func sortByTimestampDesc(conditions knative.Conditions) knative.Conditions {
sort.Slice(conditions, func(i, j int) bool {
return isCondition1BeforeCondition2(&conditions[j], &conditions[i])
})
return conditions
}

func isCondition1BeforeCondition2(c1 *knativeApis.Condition, c2 *knativeApis.Condition) bool {
return c1.LastTransitionTime.Inner.Before(&c2.LastTransitionTime.Inner)
}
50 changes: 50 additions & 0 deletions pkg/internal/wait/pipelinerun_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion pkg/models/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package models

import (
v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
"github.com/equinor/radix-tekton/pkg/internal/wait"
"github.com/equinor/radix-tekton/pkg/models/env"
tektonclient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
"k8s.io/client-go/kubernetes"
)

//Context of the pipeline
// Context of the pipeline
type Context interface {
// ProcessRadixAppConfig Load Radix config file to a ConfigMap and create RadixApplication
ProcessRadixAppConfig() error
Expand All @@ -23,4 +24,6 @@ type Context interface {
GetTektonClient() tektonclient.Interface
// GetRadixApplication Gets the RadixApplication, loaded from the config-map
GetRadixApplication() *v1.RadixApplication
// GetPipelineRunsWaiter Returns a waiter that returns when all pipelineruns have completed
GetPipelineRunsWaiter() wait.PipelineRunsCompletionWaiter
}
30 changes: 26 additions & 4 deletions pkg/pipeline/pipeline_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package pipeline

import (
"fmt"
"strings"

"github.com/equinor/radix-tekton/pkg/internal/wait"
"github.com/equinor/radix-tekton/pkg/utils/git"
"github.com/equinor/radix-tekton/pkg/utils/radix/applicationconfig"
"strings"

"github.com/equinor/radix-common/utils"
"github.com/equinor/radix-operator/pkg/apis/radix/v1"
Expand All @@ -27,6 +29,7 @@ type pipelineContext struct {
targetEnvironments map[string]bool
hash string
ownerReference *metav1.OwnerReference
waiter wait.PipelineRunsCompletionWaiter
}

func (ctx *pipelineContext) GetEnv() env.Env {
Expand All @@ -49,6 +52,10 @@ func (ctx *pipelineContext) GetRadixApplication() *v1.RadixApplication {
return ctx.radixApplication
}

func (ctx *pipelineContext) GetPipelineRunsWaiter() wait.PipelineRunsCompletionWaiter {
return ctx.waiter
}

func (ctx *pipelineContext) getEnvVars(targetEnv string) v1.EnvVarsMap {
envVarsMap := make(v1.EnvVarsMap)
ctx.setPipelineRunParamsFromBuild(envVarsMap)
Expand All @@ -75,7 +82,7 @@ func (ctx *pipelineContext) setPipelineRunParamsFromEnvironmentBuilds(targetEnv
continue
}
for envVarName, envVarVal := range buildEnv.Build.Variables {
envVarsMap[envVarName] = envVarVal //Overrides common env-vars from Spec.Build, if any
envVarsMap[envVarName] = envVarVal // Overrides common env-vars from Spec.Build, if any
}
}
}
Expand Down Expand Up @@ -131,15 +138,30 @@ func (ctx *pipelineContext) getGitHash() (string, error) {
return "", fmt.Errorf("unknown pipeline type %s", ctx.env.GetRadixPipelineType())
}

type NewPipelineContextOption func(ctx *pipelineContext)

// NewPipelineContext Create new NewPipelineContext instance
func NewPipelineContext(kubeClient kubernetes.Interface, radixClient radixclient.Interface, tektonClient tektonclient.Interface, environment env.Env) models.Context {
func NewPipelineContext(kubeClient kubernetes.Interface, radixClient radixclient.Interface, tektonClient tektonclient.Interface, environment env.Env, options ...NewPipelineContextOption) models.Context {
ownerReference := ownerreferences.GetOwnerReferenceOfJobFromLabels()
return &pipelineContext{
ctx := &pipelineContext{
kubeClient: kubeClient,
radixClient: radixClient,
tektonClient: tektonClient,
env: environment,
hash: strings.ToLower(utils.RandStringStrSeed(5, environment.GetRadixPipelineJobName())),
ownerReference: ownerReference,
waiter: wait.NewPipelineRunsCompletionWaiter(tektonClient),
}

for _, option := range options {
option(ctx)
}

return ctx
}

func WithPipelineRunsWaiter(waiter wait.PipelineRunsCompletionWaiter) NewPipelineContextOption {
return func(ctx *pipelineContext) {
ctx.waiter = waiter
}
}
Loading