diff --git a/.github/workflows/docker-build-beta-tag.yaml b/.github/workflows/docker-build-beta-tag.yaml deleted file mode 100644 index df830867..00000000 --- a/.github/workflows/docker-build-beta-tag.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: GoReleaser tag build -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+-*" - -env: - ALPINE_IMAGE: alpine:3.18.3 - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Go - uses: actions/setup-go@v4 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Release - uses: goreleaser/goreleaser-action@v4 - with: - distribution: goreleaser - version: latest - args: release -f goreleaser/.goreleaser.yml - env: - GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }} - DOCKER_BUILDX_BUILDER: "${{ steps.buildx.outputs.name }}" - DOCKER_BUILDX_CACHE_FROM: "type=gha" - DOCKER_BUILDX_CACHE_TO: "type=gha,mode=max" - ALPINE_IMAGE: ${{ env.ALPINE_IMAGE }} - - - name: Get tag - id: tag - uses: dawidd6/action-get-tag@v1 - with: - strip_v: true - - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v2 - with: - token: ${{ secrets.CI_BOT_TOKEN }} - repository: kubeshop/helm-charts - event-type: trigger-workflow-operator-pre-release - client-payload: '{"image_tag_operator": "${{ steps.tag.outputs.tag }}"}' diff --git a/.github/workflows/docker-build-develop.yaml b/.github/workflows/docker-build-develop.yaml index acc5fb89..c77248e1 100644 --- a/.github/workflows/docker-build-develop.yaml +++ b/.github/workflows/docker-build-develop.yaml @@ -2,7 +2,10 @@ name: Develop build on: push: branches: - - develop + - main + +env: + ALPINE_IMAGE: alpine:3.18.3 env: ALPINE_IMAGE: alpine:3.18.3 diff --git a/.github/workflows/docker-build-release.yaml b/.github/workflows/docker-build-release.yaml deleted file mode 100644 index 8a5f3057..00000000 --- a/.github/workflows/docker-build-release.yaml +++ /dev/null @@ -1,63 +0,0 @@ -name: Release build -on: - workflow_dispatch: - push: - branches: - - release/** - -env: - ALPINE_IMAGE: alpine:3.18.3 - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Go - uses: actions/setup-go@v4 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Output commit sha - id: github_sha - run: echo "::set-output name=sha_short::${GITHUB_SHA::7}" - - - name: Build images with GoReleaser - uses: goreleaser/goreleaser-action@v4 - with: - distribution: goreleaser - version: latest - args: release -f goreleaser/.goreleaser-snapshot.yaml --snapshot - env: - GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }} - DOCKER_BUILDX_BUILDER: "${{ steps.buildx.outputs.name }}" - DOCKER_BUILDX_CACHE_FROM: "type=gha" - DOCKER_BUILDX_CACHE_TO: "type=gha,mode=max" - ALPINE_IMAGE: ${{ env.ALPINE_IMAGE }} - - - name: Push Docker images - run: | - docker push kubeshop/testkube-operator:${{ steps.github_sha.outputs.sha_short }} - - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v2 - with: - token: ${{ secrets.CI_BOT_TOKEN }} - repository: kubeshop/helm-charts - event-type: trigger-workflow-operator-release - client-payload: '{"image_tag_operator": "${{ steps.github_sha.outputs.sha_short }}"}' \ No newline at end of file diff --git a/README.md b/README.md index 07452143..c59cdc29 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,4 @@ This is the k8s operator for [testkube](https://github.com/kubeshop/testkube/) - your friendly Kubernetes testing framework! *`Please note!`* For now it has limited functionality and only installs needed CRDs (custom resurces definitions) into an active k8s cluster. -It's meant to be installed as a part of main Helm chart as described in the [Installation](https://kubeshop.github.io/testkube/installing/) guide. +It's meant to be installed as a part of main Helm chart as described in the [Installation](https://kubeshop.github.io/testkube/installing/) guide diff --git a/pkg/client/executors/v1/mock_webhooks.go b/pkg/client/executors/v1/mock_webhooks.go new file mode 100644 index 00000000..319fb06e --- /dev/null +++ b/pkg/client/executors/v1/mock_webhooks.go @@ -0,0 +1,138 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kubeshop/testkube-operator/pkg/client/executors/v1 (interfaces: WebhooksInterface) + +// Package executors is a generated GoMock package. +package executors + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/kubeshop/testkube-operator/api/executor/v1" +) + +// MockWebhooksInterface is a mock of WebhooksInterface interface. +type MockWebhooksInterface struct { + ctrl *gomock.Controller + recorder *MockWebhooksInterfaceMockRecorder +} + +// MockWebhooksInterfaceMockRecorder is the mock recorder for MockWebhooksInterface. +type MockWebhooksInterfaceMockRecorder struct { + mock *MockWebhooksInterface +} + +// NewMockWebhooksInterface creates a new mock instance. +func NewMockWebhooksInterface(ctrl *gomock.Controller) *MockWebhooksInterface { + mock := &MockWebhooksInterface{ctrl: ctrl} + mock.recorder = &MockWebhooksInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWebhooksInterface) EXPECT() *MockWebhooksInterfaceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockWebhooksInterface) Create(arg0 *v1.Webhook) (*v1.Webhook, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0) + ret0, _ := ret[0].(*v1.Webhook) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockWebhooksInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWebhooksInterface)(nil).Create), arg0) +} + +// Delete mocks base method. +func (m *MockWebhooksInterface) Delete(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockWebhooksInterfaceMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWebhooksInterface)(nil).Delete), arg0) +} + +// DeleteByLabels mocks base method. +func (m *MockWebhooksInterface) DeleteByLabels(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByLabels", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteByLabels indicates an expected call of DeleteByLabels. +func (mr *MockWebhooksInterfaceMockRecorder) DeleteByLabels(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByLabels", reflect.TypeOf((*MockWebhooksInterface)(nil).DeleteByLabels), arg0) +} + +// Get mocks base method. +func (m *MockWebhooksInterface) Get(arg0 string) (*v1.Webhook, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0) + ret0, _ := ret[0].(*v1.Webhook) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockWebhooksInterfaceMockRecorder) Get(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockWebhooksInterface)(nil).Get), arg0) +} + +// GetByEvent mocks base method. +func (m *MockWebhooksInterface) GetByEvent(arg0 v1.EventType) (*v1.WebhookList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetByEvent", arg0) + ret0, _ := ret[0].(*v1.WebhookList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByEvent indicates an expected call of GetByEvent. +func (mr *MockWebhooksInterfaceMockRecorder) GetByEvent(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByEvent", reflect.TypeOf((*MockWebhooksInterface)(nil).GetByEvent), arg0) +} + +// List mocks base method. +func (m *MockWebhooksInterface) List(arg0 string) (*v1.WebhookList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0) + ret0, _ := ret[0].(*v1.WebhookList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockWebhooksInterfaceMockRecorder) List(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockWebhooksInterface)(nil).List), arg0) +} + +// Update mocks base method. +func (m *MockWebhooksInterface) Update(arg0 *v1.Webhook) (*v1.Webhook, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0) + ret0, _ := ret[0].(*v1.Webhook) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockWebhooksInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWebhooksInterface)(nil).Update), arg0) +} diff --git a/pkg/client/executors/v1/webhooks.go b/pkg/client/executors/v1/webhooks.go index 17173395..c3d99d1e 100644 --- a/pkg/client/executors/v1/webhooks.go +++ b/pkg/client/executors/v1/webhooks.go @@ -11,6 +11,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +//go:generate mockgen -destination=./mock_webhooks.go -package=executors "github.com/kubeshop/testkube-operator/pkg/client/executors/v1" WebhooksInterface +type WebhooksInterface interface { + List(selector string) (*executorsv1.WebhookList, error) + Get(name string) (*executorsv1.Webhook, error) + GetByEvent(event executorsv1.EventType) (*executorsv1.WebhookList, error) + Create(webhook *executorsv1.Webhook) (*executorsv1.Webhook, error) + Update(webhook *executorsv1.Webhook) (*executorsv1.Webhook, error) + Delete(name string) error + DeleteByLabels(selector string) error +} + // NewWebhooksClient returns new client instance, needs kubernetes client to be passed as dependecy func NewWebhooksClient(client client.Client, namespace string) *WebhooksClient { return &WebhooksClient{ diff --git a/pkg/client/testtriggers/v1/mock_testtriggers.go b/pkg/client/testtriggers/v1/mock_testtriggers.go new file mode 100644 index 00000000..3f52fa1b --- /dev/null +++ b/pkg/client/testtriggers/v1/mock_testtriggers.go @@ -0,0 +1,137 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kubeshop/testkube-operator/pkg/client/testtriggers/v1 (interfaces: Interface) + +// Package v1 is a generated GoMock package. +package v1 + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1" +) + +// MockInterface is a mock of Interface interface. +type MockInterface struct { + ctrl *gomock.Controller + recorder *MockInterfaceMockRecorder +} + +// MockInterfaceMockRecorder is the mock recorder for MockInterface. +type MockInterfaceMockRecorder struct { + mock *MockInterface +} + +// NewMockInterface creates a new mock instance. +func NewMockInterface(ctrl *gomock.Controller) *MockInterface { + mock := &MockInterface{ctrl: ctrl} + mock.recorder = &MockInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockInterface) Create(arg0 *v1.TestTrigger) (*v1.TestTrigger, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0) + ret0, _ := ret[0].(*v1.TestTrigger) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockInterfaceMockRecorder) Create(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockInterface)(nil).Create), arg0) +} + +// Delete mocks base method. +func (m *MockInterface) Delete(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockInterfaceMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInterface)(nil).Delete), arg0, arg1) +} + +// DeleteAll mocks base method. +func (m *MockInterface) DeleteAll(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAll", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAll indicates an expected call of DeleteAll. +func (mr *MockInterfaceMockRecorder) DeleteAll(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAll", reflect.TypeOf((*MockInterface)(nil).DeleteAll), arg0) +} + +// DeleteByLabels mocks base method. +func (m *MockInterface) DeleteByLabels(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteByLabels", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteByLabels indicates an expected call of DeleteByLabels. +func (mr *MockInterfaceMockRecorder) DeleteByLabels(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByLabels", reflect.TypeOf((*MockInterface)(nil).DeleteByLabels), arg0, arg1) +} + +// Get mocks base method. +func (m *MockInterface) Get(arg0, arg1 string) (*v1.TestTrigger, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(*v1.TestTrigger) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockInterfaceMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockInterface)(nil).Get), arg0, arg1) +} + +// List mocks base method. +func (m *MockInterface) List(arg0, arg1 string) (*v1.TestTriggerList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1) + ret0, _ := ret[0].(*v1.TestTriggerList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockInterfaceMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockInterface)(nil).List), arg0, arg1) +} + +// Update mocks base method. +func (m *MockInterface) Update(arg0 *v1.TestTrigger) (*v1.TestTrigger, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0) + ret0, _ := ret[0].(*v1.TestTrigger) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockInterfaceMockRecorder) Update(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockInterface)(nil).Update), arg0) +} diff --git a/pkg/client/testtriggers/v1/testtriggers.go b/pkg/client/testtriggers/v1/testtriggers.go new file mode 100644 index 00000000..26677261 --- /dev/null +++ b/pkg/client/testtriggers/v1/testtriggers.go @@ -0,0 +1,120 @@ +package v1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + + testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1" +) + +//go:generate mockgen -destination=./mock_testtriggers.go -package=v1 "github.com/kubeshop/testkube-operator/pkg/client/testtriggers/v1" Interface +type Interface interface { + List(selector, namespace string) (*testtriggersv1.TestTriggerList, error) + Get(name, namespace string) (*testtriggersv1.TestTrigger, error) + Create(trigger *testtriggersv1.TestTrigger) (*testtriggersv1.TestTrigger, error) + Update(trigger *testtriggersv1.TestTrigger) (*testtriggersv1.TestTrigger, error) + Delete(name, namespace string) error + DeleteAll(namespace string) error + DeleteByLabels(selector, namespace string) error +} + +// NewClient creates new TestTrigger client +func NewClient(client client.Client, namespace string) *TestTriggersClient { + return &TestTriggersClient{ + Client: client, + Namespace: namespace, + } +} + +// TestTriggersClient implements methods to work with TestTriggers +type TestTriggersClient struct { + Client client.Client + Namespace string +} + +// List lists TestTriggers +func (s TestTriggersClient) List(selector, namespace string) (*testtriggersv1.TestTriggerList, error) { + list := &testtriggersv1.TestTriggerList{} + reqs, err := labels.ParseToRequirements(selector) + if err != nil { + return list, err + } + if namespace == "" { + namespace = s.Namespace + } + + options := &client.ListOptions{ + Namespace: namespace, + LabelSelector: labels.NewSelector().Add(reqs...), + } + + if err = s.Client.List(context.Background(), list, options); err != nil { + return list, err + } + + return list, nil +} + +// Get returns TestTrigger +func (s TestTriggersClient) Get(name, namespace string) (*testtriggersv1.TestTrigger, error) { + if namespace == "" { + namespace = s.Namespace + } + trigger := &testtriggersv1.TestTrigger{} + err := s.Client.Get(context.Background(), client.ObjectKey{Namespace: namespace, Name: name}, trigger) + if err != nil { + return nil, err + } + return trigger, nil +} + +// Create creates new TestTrigger +func (s TestTriggersClient) Create(trigger *testtriggersv1.TestTrigger) (*testtriggersv1.TestTrigger, error) { + return trigger, s.Client.Create(context.Background(), trigger) +} + +// Update updates existing TestTrigger +func (s TestTriggersClient) Update(trigger *testtriggersv1.TestTrigger) (*testtriggersv1.TestTrigger, error) { + return trigger, s.Client.Update(context.Background(), trigger) +} + +// Delete deletes existing TestTrigger +func (s TestTriggersClient) Delete(name, namespace string) error { + trigger, err := s.Get(name, namespace) + if err != nil { + return err + } + return s.Client.Delete(context.Background(), trigger) +} + +// DeleteAll delete all TestTriggers +func (s TestTriggersClient) DeleteAll(namespace string) error { + if namespace == "" { + namespace = s.Namespace + } + u := &unstructured.Unstructured{} + u.SetKind("TestTrigger") + u.SetAPIVersion(testtriggersv1.GroupVersion.String()) + return s.Client.DeleteAllOf(context.Background(), u, client.InNamespace(namespace)) +} + +// DeleteByLabels deletes TestTriggers by labels +func (s TestTriggersClient) DeleteByLabels(selector, namespace string) error { + reqs, err := labels.ParseToRequirements(selector) + if err != nil { + return err + } + if namespace == "" { + namespace = s.Namespace + } + + u := &unstructured.Unstructured{} + u.SetKind("TestTrigger") + u.SetAPIVersion(testtriggersv1.GroupVersion.String()) + err = s.Client.DeleteAllOf(context.Background(), u, client.InNamespace(namespace), + client.MatchingLabelsSelector{Selector: labels.NewSelector().Add(reqs...)}) + return err +}