├── numaplane-controller.tar
├── tests
├── manifests
│ ├── default
│ │ ├── prometheus-ns.yaml
│ │ ├── numaplane-ns.yaml
│ │ ├── rollouts-ns.yaml
│ │ ├── kustomization.yaml
│ │ ├── config.yaml
│ │ └── prometheus-monitors.yaml
│ └── e2e
│ │ ├── special-cases
│ │ ├── no-strategy
│ │ │ ├── kustomization.yaml
│ │ │ └── controller-config.yaml
│ │ └── pause-and-drain
│ │ │ ├── kustomization.yaml
│ │ │ └── controller-config.yaml
│ │ └── default
│ │ └── kustomization.yaml
├── e2e
│ ├── coverage
│ │ ├── entrypoint.sh
│ │ └── Dockerfile
│ └── analysistemplate.go
└── config
│ ├── testconfig2.yaml
│ └── testconfig.yaml
├── extensions
└── argo
│ └── numa-rollout
│ └── numa-rollout-extension
│ ├── ui
│ ├── src
│ │ ├── react-app-env.d.ts
│ │ ├── rollout
│ │ │ ├── default
│ │ │ │ ├── ArgoSummary.css
│ │ │ │ ├── ArgoRolloutSummary.test.tsx
│ │ │ │ ├── ArgoRolloutComponent.tsx
│ │ │ │ ├── ArgoRolloutComponent.test.tsx
│ │ │ │ ├── ArgoRolloutContainers.test.tsx
│ │ │ │ ├── ArgoRolloutComponent.css
│ │ │ │ ├── ArgoRolloutSummary.tsx
│ │ │ │ └── ArgoRolloutContainers.tsx
│ │ │ ├── ISBRollout.test.tsx
│ │ │ ├── PipelineRollout.test.tsx
│ │ │ ├── MonovertexRollout.test.tsx
│ │ │ ├── ControllerRollout.test.tsx
│ │ │ ├── RolloutComponentWrapper.test.tsx
│ │ │ ├── PipelineRollout.tsx
│ │ │ ├── MonovertexRollout.tsx
│ │ │ ├── ISBRollout.tsx
│ │ │ └── ControllerRollout.tsx
│ │ ├── setupTests.ts
│ │ ├── index.css
│ │ ├── utils
│ │ │ ├── SquareCancelIcon.tsx
│ │ │ ├── SquareCheckIcon.tsx
│ │ │ ├── SquareCheckIcon.test.tsx
│ │ │ ├── SquareCancelIcon.test.tsx
│ │ │ └── Constants.tsx
│ │ └── index.tsx
│ ├── extension.tar
│ ├── dist
│ │ ├── extension.tar.gz
│ │ └── resources
│ │ │ └── extension-Numarollout.js
│ │ │ └── extensions-Numarollout.js.LICENSE.txt
│ ├── README.md
│ ├── Makefile
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── webpack.config.js
│ └── package.json
│ ├── package.json
│ └── yarn.lock
├── .dockerignore
├── .codecov.yml
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
├── workflows
│ ├── pr.yaml
│ └── release.yml
└── pull_request_template.md
├── internal
├── util
│ ├── kubernetes
│ │ ├── testdata
│ │ │ ├── svc.yaml
│ │ │ └── svc-with-invalid-data.yaml
│ │ ├── kubectl.go
│ │ ├── clientset.go
│ │ ├── structured_util_test.go
│ │ ├── structured_util.go
│ │ └── kubernetes_util.go
│ ├── metrics
│ │ └── transportwrapper.go
│ ├── queue.go
│ ├── logger
│ │ └── base_logger.go
│ └── maps.go
├── sync
│ └── testdata
│ │ ├── pipeline-config.yaml
│ │ └── pipeline-live.yaml
└── controller
│ ├── config
│ ├── options.go
│ ├── user_config.go
│ └── usde_config.go
│ ├── common
│ ├── rollout_object.go
│ ├── rollout_object_test.go
│ ├── numaflowtypes
│ │ ├── vertex.go
│ │ ├── monovertex.go
│ │ └── isbsvc.go
│ ├── predicate_filters.go
│ └── in_progress_strategy_test.go
│ └── numaflowcontroller
│ └── numaflowcontroller_controller_test.go
├── config
├── rbac
│ ├── service_account.yaml
│ ├── leader_election_role_binding.yaml
│ ├── role_binding.yaml
│ ├── pipelinerollout_viewer_role.yaml
│ ├── isbservicerollout_viewer_role.yaml
│ ├── monovertexrollout_viewer_role.yaml
│ ├── numaflowcontrollerrollout_viewer_role.yaml
│ ├── numaflowcontroller_viewer_role.yaml
│ ├── pipelinerollout_editor_role.yaml
│ ├── monovertexrollout_editor_role.yaml
│ ├── isbservicerollout_editor_role.yaml
│ ├── numaflowcontrollerrollout_editor_role.yaml
│ ├── numaflowcontroller_editor_role.yaml
│ ├── numaplane-aggregate-to-view.yaml
│ ├── kustomization.yaml
│ ├── numaplane-aggregate-to-edit.yaml
│ ├── numaplane-aggregate-to-admin.yaml
│ ├── leader_election_role.yaml
│ └── role.yaml
├── default
│ ├── kustomization.yaml
│ └── manager_config_patch.yaml
├── manager
│ ├── kustomization.yaml
│ └── controller-config.yaml
├── samples
│ ├── numaplane.numaproj.io_v1alpha1_numaflowcontroller.yaml
│ ├── kustomization.yaml
│ ├── numaplane.numaproj.io_v1alpha1_numaflowcontrollerrollout.yaml
│ ├── namespace-level-config.yaml
│ ├── numaplane.numaproj.io_v1alpha1_isbservicerollout.yaml
│ └── numaplane.numaproj.io_v1alpha1_pipelinerollout.yaml
├── kustomize
│ └── examples
│ │ └── transformer
│ │ ├── my-isbservicerollout.yaml
│ │ ├── my-monovertexrollout.yaml
│ │ ├── kustomization.yaml
│ │ └── my-pipelinerollout.yaml
└── crd
│ ├── kustomization.yaml
│ └── kustomizeconfig.yaml
├── pkg
├── apis
│ └── numaplane
│ │ └── v1alpha1
│ │ ├── const.go
│ │ ├── numaflowcontroller_types.go
│ │ ├── groupversion_info.go
│ │ └── numaflowcontrollerrollout_types.go
└── client
│ ├── clientset
│ └── versioned
│ │ ├── fake
│ │ ├── doc.go
│ │ ├── register.go
│ │ └── clientset_generated.go
│ │ ├── scheme
│ │ ├── doc.go
│ │ └── register.go
│ │ └── typed
│ │ └── numaplane
│ │ └── v1alpha1
│ │ ├── fake
│ │ ├── doc.go
│ │ └── fake_numaplane_client.go
│ │ ├── doc.go
│ │ ├── generated_expansion.go
│ │ ├── pipelinerollout.go
│ │ ├── isbservicerollout.go
│ │ ├── monovertexrollout.go
│ │ └── numaflowcontroller.go
│ ├── informers
│ └── externalversions
│ │ ├── internalinterfaces
│ │ └── factory_interfaces.go
│ │ ├── numaplane
│ │ ├── interface.go
│ │ └── v1alpha1
│ │ │ └── interface.go
│ │ └── generic.go
│ └── listers
│ └── numaplane
│ └── v1alpha1
│ ├── expansion_generated.go
│ ├── pipelinerollout.go
│ ├── isbservicerollout.go
│ ├── monovertexrollout.go
│ ├── numaflowcontroller.go
│ └── numaflowcontrollerrollout.go
├── hack
├── numaflow-controller-def-generator
│ ├── def-configmap.yaml
│ └── kustomization.yaml
├── boilerplate.go.txt
├── update-codegen.sh
├── tools.go
└── library.sh
├── .gitignore
├── Dockerfile
├── PROJECT
└── README.md
/numaplane-controller.tar:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/manifests/default/prometheus-ns.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: prometheus
--------------------------------------------------------------------------------
/tests/manifests/default/numaplane-ns.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: numaplane-system
--------------------------------------------------------------------------------
/tests/manifests/default/rollouts-ns.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: argo-rollouts
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore build and test binaries.
3 | bin/
4 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "react-test-renderer": "^18.2.0"
4 | }
5 | }
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoSummary.css:
--------------------------------------------------------------------------------
1 | .info-item{
2 | width: 86px;
3 | justify-content: center;
4 | }
--------------------------------------------------------------------------------
/.codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "pkg/client/.*"
3 | - "tests/.*"
4 | - "**/*generated.deepcopy.go"
5 | - "internal/controller/common/test_common.go"
6 | - "vendor/.*"
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in
2 | # the repo. Unless a later match takes precedence
3 | * @xdevxy @juliev0 @afugazzotto @dsimha @shakira
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/extension.tar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/numaproj/numaplane/HEAD/extensions/argo/numa-rollout/numa-rollout-extension/ui/extension.tar
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/dist/extension.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/numaproj/numaplane/HEAD/extensions/argo/numa-rollout/numa-rollout-extension/ui/dist/extension.tar.gz
--------------------------------------------------------------------------------
/tests/manifests/e2e/special-cases/no-strategy/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - ../../default
6 |
7 | patches:
8 | - path: controller-config.yaml
--------------------------------------------------------------------------------
/tests/manifests/e2e/special-cases/pause-and-drain/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - ../../default
6 |
7 | patches:
8 | - path: controller-config.yaml
--------------------------------------------------------------------------------
/internal/util/kubernetes/testdata/svc.yaml:
--------------------------------------------------------------------------------
1 | kind: Service
2 | apiVersion: v1
3 | metadata:
4 | name: my-service
5 | spec:
6 | selector:
7 | app: MyApp
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 9376
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: serviceaccount
6 | app.kubernetes.io/component: rbac
7 | app.kubernetes.io/part-of: numaplane
8 | name: numaplane-sa
--------------------------------------------------------------------------------
/config/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | namespace: numaplane-system
5 |
6 | resources:
7 | - ../crd
8 | - ../rbac
9 | - ../manager
10 |
11 | patches:
12 | - path: manager_config_patch.yaml
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - controller-manager.yaml
6 | - controller-config.yaml
7 | - usde-config.yaml
8 |
9 | images:
10 | - name: quay.io/numaproj/numaplane-controller
11 | newTag: latest
12 |
--------------------------------------------------------------------------------
/config/samples/numaplane.numaproj.io_v1alpha1_numaflowcontroller.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: NumaflowController
3 | metadata:
4 | name: numaflowcontroller-sample
5 | namespace: example-namespace
6 | spec:
7 | #instanceID: "0" # uncomment for Progressive rollout to set Numaflow Controller instance
8 | version: "1.5.2"
9 |
--------------------------------------------------------------------------------
/config/kustomize/examples/transformer/my-isbservicerollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: ISBServiceRollout
3 | metadata:
4 | name: simple-isbservice
5 | spec:
6 | interStepBufferService:
7 | spec:
8 | jetstream:
9 | replicas: 1
10 | version: latest
11 | persistence:
12 | volumeSize: 1Gi
13 |
--------------------------------------------------------------------------------
/pkg/apis/numaplane/v1alpha1/const.go:
--------------------------------------------------------------------------------
1 | package v1alpha1
2 |
3 | const (
4 | RolloutISBSvcName = "isbsvc-rollout"
5 | RolloutPipelineName = "pipeline-rollout"
6 | RolloutNumaflowControllerName = "numaflow-controller-rollout"
7 | RolloutMonoVertexName = "monovertex-rollout"
8 | NumaflowControllerName = "numaflow-controller"
9 | )
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
3 | contact_links:
4 | - name: Have you read the docs?
5 | url: https://github.com/numaproj-labs/numaplane
6 | about: Much help can be found in the docs
7 | - name: Ask a question
8 | url: https://github.com/numaproj-labs/numaplane/discussions/new
9 | about: Ask a question or start a discussion about Numaplane
--------------------------------------------------------------------------------
/config/default/manager_config_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: numaplane-controller-manager
5 | spec:
6 | template:
7 | spec:
8 | containers:
9 | - name: manager
10 | args:
11 | - "--health-probe-bind-address=:8081"
12 | - "--metrics-bind-address=:8080"
13 | - "--leader-elect"
--------------------------------------------------------------------------------
/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples of your project ##
2 | resources:
3 | - numaplane.numaproj.io_v1alpha1_numaflowcontrollerrollout.yaml
4 | - numaplane.numaproj.io_v1alpha1_numaflowcontroller.yaml
5 | - numaplane.numaproj.io_v1alpha1_isbservicerollout.yaml
6 | - numaplane.numaproj.io_v1alpha1_pipelinerollout.yaml
7 | - namespace-level-config.yaml
8 | #+kubebuilder:scaffold:manifestskustomizesamples
9 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/README.md:
--------------------------------------------------------------------------------
1 | ## Available Scripts
2 |
3 | In the project directory, you can run:
4 |
5 | ### `yarn build`
6 |
7 | copy the contents in the extensions-Numarollout.js file and paste it in the console of the page running the ArgoCD UI.
8 |
9 | ### `yarn test`
10 |
11 | Runs unit tests inside the UI folder. i.e. the files with extension test.tsx
12 |
13 |
14 |
--------------------------------------------------------------------------------
/config/samples/numaplane.numaproj.io_v1alpha1_numaflowcontrollerrollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: NumaflowControllerRollout
3 | metadata:
4 | name: numaflow-controller
5 | #name: numaflow-controller-0
6 | namespace: example-namespace
7 | spec:
8 | controller:
9 | #instanceID: "0" # uncomment for Progressive rollout to set Numaflow Controller instance
10 | version: "1.5.2"
11 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/testdata/svc-with-invalid-data.yaml:
--------------------------------------------------------------------------------
1 | kind: Service
2 | apiVersion: v1
3 | metadata:
4 | name: my-service
5 | annotations:
6 | valid-annotation: existing-value
7 | invalid-annotation: null
8 | labels:
9 | valid-label: existing-value
10 | invalid-label: null
11 | spec:
12 | selector:
13 | app: MyApp
14 | ports:
15 | - protocol: TCP
16 | port: 80
17 | targetPort: 9376
--------------------------------------------------------------------------------
/.github/workflows/pr.yaml:
--------------------------------------------------------------------------------
1 | name: PR
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 |
7 | permissions:
8 | contents: read
9 |
10 | jobs:
11 | title-check:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Check PR Title's semantic conformance
15 | uses: Slashgear/action-check-pr-title@v4.3.0
16 | with:
17 | regexp: "(feat|fix|docs|chore):.+" # Regex the title should match.
18 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | yarn build
3 | # dist/extension.tar must be committed into repo for install.yml to reference
4 |
5 | reapply:
6 | kubectl delete -f ../manifests/install.yml -n argocd
7 | kubectl apply -f ../manifests/install.yml -n argocd
8 |
9 | apply:
10 | kubectl apply -f ../manifests/install.yml -n argocd
11 |
12 | delete:
13 | kubectl delete -f ../manifests/install.yml -n argocd
--------------------------------------------------------------------------------
/hack/numaflow-controller-def-generator/def-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: numaflow-controller-definitions-$TMP_NFC_DEF_GEN_NUMAFLOW_VERSION
5 | namespace: numaplane-system
6 | labels:
7 | "numaplane.numaproj.io/config": numaflow-controller-definitions
8 | data:
9 | controller_definitions.yaml: |
10 | controllerDefinitions:
11 | - version: "$TMP_NFC_DEF_GEN_NUMAFLOW_VERSION"
12 | fullSpec: |
13 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
7 | module.exports = {
8 | // other Jest configuration options
9 | transformIgnorePatterns: ['/node_modules/(?!@testing-library\\/dom)'],
10 | };
11 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: rolebinding
6 | app.kubernetes.io/component: rbac
7 | app.kubernetes.io/part-of: numaplane
8 | name: numaplane-leader-election-rolebinding
9 | roleRef:
10 | apiGroup: rbac.authorization.k8s.io
11 | kind: Role
12 | name: numaplane-leader-election-role
13 | subjects:
14 | - kind: ServiceAccount
15 | name: numaplane-sa
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: clusterrolebinding
6 | app.kubernetes.io/component: rbac
7 | app.kubernetes.io/part-of: numaplane
8 | name: numaplane-rolebinding
9 | roleRef:
10 | apiGroup: rbac.authorization.k8s.io
11 | kind: ClusterRole
12 | name: numaplane-role
13 | subjects:
14 | - kind: ServiceAccount
15 | name: numaplane-sa
16 | namespace: numaplane-system
--------------------------------------------------------------------------------
/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - bases/numaplane.numaproj.io_pipelinerollouts.yaml
6 | - bases/numaplane.numaproj.io_numaflowcontrollerrollouts.yaml
7 | - bases/numaplane.numaproj.io_isbservicerollouts.yaml
8 | - bases/numaplane.numaproj.io_monovertexrollouts.yaml
9 | - bases/numaplane.numaproj.io_numaflowcontrollers.yaml
10 | # install numaflow minimal CRDs as a dependency
11 | - https://github.com/numaproj/numaflow/config/advanced-install/minimal-crds?ref=v1.5.2
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Propose an enhancement for this project
4 | labels: 'enhancement'
5 | ---
6 | # Summary
7 |
8 | What change needs making?
9 |
10 | # Use Cases
11 |
12 | When would you use this?
13 |
14 | ---
15 |
16 | **Message from the maintainers**:
17 |
18 | If you wish to see this enhancement implemented please add a 👍 reaction to this issue! We often sort issues this way to know what to prioritize.
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutSummary.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import { ArgoRolloutSummary } from "./ArgoRolloutSummary";
3 |
4 | const rolloutParams = {
5 | strategy: "blueGreen",
6 | setWeight: 10,
7 | actualWeight: 100,
8 | };
9 | describe("ArgoRolloutSummary", () => {
10 | it("should render", () => {
11 | render();
12 | expect(screen.getByText("blueGreen")).toBeInTheDocument();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/config/samples/namespace-level-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: my-config
5 | namespace: example-namespace
6 | labels:
7 | numaplane.numaproj.io/config: namespace-level-config
8 | data:
9 | # TODO-PROGRESSIVE: before the PROGRESSIVE strategy is implemented, users will only be able to choose "pause-and-drain". Afterwards, "progressive" should also be an option. Remove this comment line after implementing PROGRESSIVE strategy.
10 | # upgradeStrategy can be either "progressive" or "pause-and-drain"
11 | upgradeStrategy: "pause-and-drain"
12 |
--------------------------------------------------------------------------------
/tests/e2e/coverage/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # Start the main process in the background
5 | /manager --health-probe-bind-address=:8081 --metrics-bind-address=:8080 --leader-elect &
6 | export NUMAPLANE_PID=$!
7 | ## Write NUMAPLANE_PID to a file
8 | echo $NUMAPLANE_PID > /numaplane.pid
9 |
10 | # Function to handle signals
11 | trap "echo 'Stopping PID $PID'; kill $PID; wait $PID; /manager --health-probe-bind-address=:8081 --metrics-bind-address=:8080 --leader-elect &" SIGTERM SIGINT
12 |
13 | # Wait indefinitely
14 | while true
15 | do
16 | tail -f /dev/null & wait ${!}
17 | done
--------------------------------------------------------------------------------
/config/rbac/pipelinerollout_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view pipelinerollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: pipelinerollout-viewer-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - pipelinerollouts
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - numaplane.numaproj.io
20 | resources:
21 | - pipelinerollouts/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/config/rbac/isbservicerollout_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view isbservicerollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: isbservicerollout-viewer-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - isbservicerollouts
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - numaplane.numaproj.io
20 | resources:
21 | - isbservicerollouts/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/config/rbac/monovertexrollout_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view monovertexrollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: monovertexrollout-viewer-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - monovertexrollouts
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - numaplane.numaproj.io
20 | resources:
21 | - monovertexrollouts/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/kubectl.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/argoproj/gitops-engine/pkg/utils/kube"
7 | "github.com/argoproj/gitops-engine/pkg/utils/tracing"
8 |
9 | "github.com/numaproj/numaplane/internal/util/logger"
10 | )
11 |
12 | var tracer tracing.Tracer = &tracing.NopTracer{}
13 |
14 | func init() {
15 | if os.Getenv("NUMAPLANE_TRACING_ENABLED") == "1" {
16 | tracer = tracing.NewLoggingTracer(*logger.New().LogrLogger)
17 | }
18 | }
19 |
20 | func NewKubectl() kube.Kubectl {
21 | return &kube.KubectlCmd{Tracer: tracer, Log: *logger.New().LogrLogger}
22 | }
23 |
--------------------------------------------------------------------------------
/tests/config/testconfig2.yaml:
--------------------------------------------------------------------------------
1 | logLevel: DEBUG # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
2 | defaultUpgradeStrategy: "progressive"
3 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=,kind=ConfigMap;group=autoscaling,kind=HorizontalPodAutoscaler"
4 | progressive:
5 | defaultAssessmentSchedule:
6 | - kind: Pipeline
7 | schedule: "120,360,0,10"
8 | - kind: MonoVertex
9 | schedule: "120,360,0,10"
10 | - kind: InterstepBufferService
11 | schedule: "0,480,0,10"
12 | analysisRunTimeout: "600"
13 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
--------------------------------------------------------------------------------
/hack/numaflow-controller-def-generator/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | commonLabels:
4 | app.kubernetes.io/instance: '{{ .InstanceID }}'
5 | nameSuffix: "{{ .InstanceSuffix }}"
6 |
7 | resources:
8 | - namespace-install.yaml
9 |
10 | patches:
11 | # Remove all CRDs
12 | - target:
13 | group: apiextensions.k8s.io
14 | version: v1
15 | kind: CustomResourceDefinition
16 | name: .*
17 | patch: |-
18 | $patch: delete
19 | apiVersion: apiextensions.k8s.io/v1
20 | kind: CustomResourceDefinition
21 | metadata:
22 | name: not-important
23 |
--------------------------------------------------------------------------------
/config/rbac/numaflowcontrollerrollout_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view numaflowcontrollerrollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: numaflowcontrollerrollout-viewer-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - numaflowcontrollerrollouts
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - numaplane.numaproj.io
20 | resources:
21 | - numaflowcontrollerrollouts/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/config/rbac/numaflowcontroller_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view numaflowcontrollers.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: numaflowcontroller-viewer-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io.github.com.numaproj
12 | resources:
13 | - numaflowcontrollers
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - numaplane.numaproj.io.github.com.numaproj
20 | resources:
21 | - numaflowcontrollers/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/config/rbac/pipelinerollout_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit pipelinerollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: pipelinerollout-editor-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - pipelinerollouts
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - numaplane.numaproj.io
24 | resources:
25 | - pipelinerollouts/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/internal/util/metrics/transportwrapper.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "github.com/argoproj/pkg/kubeclientmetrics"
5 | "k8s.io/client-go/rest"
6 | )
7 |
8 | // AddMetricsTransportWrapper adds a transport wrapper which increments 'numaplane_app_k8s_request_total' counter on each kubernetes request
9 | func AddMetricsTransportWrapper(metrics *CustomMetrics, config *rest.Config) *rest.Config {
10 | fn := func(resourceInfo kubeclientmetrics.ResourceInfo) error {
11 | metrics.KubeRequestCounter.WithLabelValues().Inc()
12 | return nil
13 | }
14 |
15 | newConfig := kubeclientmetrics.AddMetricsTransportWrapper(config, fn)
16 | return newConfig
17 | }
18 |
--------------------------------------------------------------------------------
/config/rbac/monovertexrollout_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit monovertexrolouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: monovertexrollout-editor-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - monovertexrollouts
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - numaplane.numaproj.io
24 | resources:
25 | - monovertexrollouts/status
26 | verbs:
27 | - get
--------------------------------------------------------------------------------
/config/rbac/isbservicerollout_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit isbservicerollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: isbservicerollout-editor-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - isbservicerollouts
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - numaplane.numaproj.io
24 | resources:
25 | - isbservicerollouts/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/internal/sync/testdata/pipeline-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaflow.numaproj.io/v1alpha1
2 | kind: Pipeline
3 | metadata:
4 | name: simple-pipeline
5 | spec:
6 | vertices:
7 | - name: in
8 | source:
9 | # A self data generating source
10 | generator:
11 | rpu: 5
12 | duration: 1s
13 | - name: cat
14 | udf:
15 | container:
16 | image: "quay.io/numaio/numaflow-go/map-cat:stable"
17 | imagePullPolicy: Always,
18 | - name: out
19 | sink:
20 | # A simple log printing sink
21 | log: {}
22 | edges:
23 | - from: in
24 | to: cat
25 | - from: cat
26 | to: out
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/ISBRollout.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/react";
2 |
3 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
4 | import { mockISBRolloutProps } from "../../mocks/mockProps";
5 | import { ISBRollout } from "./ISBRollout";
6 | describe("ISB Rollout", () => {
7 | it("should render", () => {
8 | const { getByText } = render(
9 |
10 |
11 |
12 | );
13 | expect(getByText("ISB Name : my-isbsvc")).toBeInTheDocument();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/config/rbac/numaflowcontrollerrollout_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit numaflowcontrollerrollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: numaflowcontrollerrollout-editor-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io
12 | resources:
13 | - numaflowcontrollerrollouts
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - numaplane.numaproj.io
24 | resources:
25 | - numaflowcontrollerrollouts/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/PipelineRollout.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/react";
2 |
3 | import { PipelineRollout } from "./PipelineRollout";
4 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
5 | import { mockISBRolloutProps } from "../../mocks/mockProps";
6 | describe("PipelineRollout", () => {
7 | it("should render", () => {
8 | const { getByText } = render(
9 |
10 |
11 |
12 | );
13 | expect(getByText("Pipeline Name: my-pipeline")).toBeInTheDocument();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/config/rbac/numaflowcontroller_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit numaflowcontrollers.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: numaplane
7 | app.kubernetes.io/managed-by: kustomize
8 | name: numaflowcontroller-editor-role
9 | rules:
10 | - apiGroups:
11 | - numaplane.numaproj.io.github.com.numaproj
12 | resources:
13 | - numaflowcontrollers
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - numaplane.numaproj.io.github.com.numaproj
24 | resources:
25 | - numaflowcontrollers/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/samples/numaplane.numaproj.io_v1alpha1_isbservicerollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: ISBServiceRollout
3 | metadata:
4 | name: my-isbsvc
5 | namespace: example-namespace
6 | spec:
7 | strategy:
8 | progressive:
9 | assessmentSchedule: "0,0,0,10"
10 | interStepBufferService:
11 | #uncomment for Progressive rollout to set Numaflow Controller instance:
12 | #metadata:
13 | # annotations:
14 | # numaflow.numaproj.io/instance: "0"
15 | spec:
16 | # Example from https://github.com/numaproj/numaflow/blob/main/examples/0-isbsvc-jetstream.yaml
17 | jetstream:
18 | version: 2.10.3
19 | persistence:
20 | volumeSize: 1Gi
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Fixes #TODO
6 |
7 | ### Modifications
8 |
9 |
10 |
11 |
12 | ### Verification
13 |
14 |
15 |
16 | ### Backward incompatibilities
17 |
18 |
19 |
--------------------------------------------------------------------------------
/config/kustomize/examples/transformer/my-monovertexrollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: MonoVertexRollout
3 | metadata:
4 | name: simple-monovertex
5 | spec:
6 | monoVertex:
7 | spec:
8 | source:
9 | udsource:
10 | container:
11 | image: quay.io/numaio/numaflow-java/source-simple-source:stable
12 | # transformer is an optional container to do any transformation to the incoming data before passing to the sink
13 | transformer:
14 | container:
15 | image: quay.io/numaio/numaflow-rs/source-transformer-now:stable
16 | sink:
17 | udsink:
18 | container:
19 | image: quay.io/numaio/numaflow-java/simple-sink:stable
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutComponent.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import "./ArgoRolloutComponent.css";
4 | import { ArgoRolloutSummary } from "./ArgoRolloutSummary";
5 | import { CANARY } from "../../utils/Constants";
6 | import { ArgoRolloutContainers } from "./ArgoRolloutContainers";
7 | import { Box } from "@mui/material";
8 |
9 | const rolloutParams = {
10 | strategy: CANARY,
11 | setWeight: "0",
12 | actualWeight: "100",
13 | step: "1",
14 | };
15 | export const ArgoRolloutComponent = () => {
16 | return (
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint:recommended", "tslint-react", "tslint-plugin-prettier", "tslint-config-prettier"
4 | ],
5 | "jsRules": {},
6 | "rules": {
7 | "prettier": true,
8 | "quotemark": [true, "single"],
9 | "no-var-requires": false,
10 | "interface-name": false,
11 | "jsx-no-multiline-js": false,
12 | "object-literal-sort-keys": false,
13 | "jsx-alignment": false,
14 | "max-line-length": [true, 200],
15 | "jsx-no-lambda": false,
16 | "array-type": false,
17 | "max-classes-per-file": false,
18 | "newline-per-chained-call": false
19 | },
20 | "rulesDirectory": []
21 | }
--------------------------------------------------------------------------------
/tests/manifests/e2e/special-cases/pause-and-drain/controller-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: numaplane-controller-config
5 | data:
6 | config.yaml: |
7 | # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
8 | logLevel: DEBUG
9 | includedResources: "group=apps,kind=Deployment;group=,kind=ConfigMap;group=,kind=ServiceAccount;group=,kind=Secret;group=,kind=Service;group=rbac.authorization.k8s.io,kind=RoleBinding;group=rbac.authorization.k8s.io,kind=Role"
10 | defaultUpgradeStrategy: "pause-and-drain"
11 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=,kind=ConfigMap;group=autoscaling,kind=HorizontalPodAutoscaler"
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you prefer the allow list template instead of the deny list, see community template:
2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3 | #
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 | bin/*
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories
19 | vendor/
20 |
21 | # editor and IDE paraphernalia
22 | .idea
23 | .vscode
24 | *.swp
25 | *.swo
26 | *~
27 | **/.DS_Store
28 |
29 | # Go workspace file
30 | go.work
31 |
32 | config/crd/bases/_.yaml
33 | .idea
34 |
35 | config/crd/external/
36 |
37 | tests/e2e/output
38 |
39 | .qodo
40 |
--------------------------------------------------------------------------------
/tests/manifests/e2e/special-cases/no-strategy/controller-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: numaplane-controller-config
5 | data:
6 | config.yaml: |
7 | # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
8 | logLevel: DEBUG
9 | includedResources: "group=apps,kind=Deployment;\
10 | group=,kind=ConfigMap;group=,kind=ServiceAccount;group=,kind=Secret;group=,kind=Service;\
11 | group=rbac.authorization.k8s.io,kind=RoleBinding;group=rbac.authorization.k8s.io,kind=Role"
12 | defaultUpgradeStrategy: "no-strategy"
13 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=,kind=ConfigMap;group=autoscaling,kind=HorizontalPodAutoscaler"
14 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/fake/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | // This package has the automatically generated fake clientset.
19 | package fake
20 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/MonovertexRollout.test.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render } from "@testing-library/react";
3 | import { MonovertexRollout } from "./MonovertexRollout";
4 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
5 | import { mockMonovertexRolloutProps } from "../../mocks/mockProps";
6 | describe("MonovertexRollout", () => {
7 | it("should render", () => {
8 | const { getByText } = render(
9 |
12 |
13 |
14 | );
15 | expect(getByText("Monovertex Rollout Name: my-mono")).toBeInTheDocument();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/ControllerRollout.test.tsx:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/react";
2 |
3 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
4 | import { mockControllerRolloutProps } from "../../mocks/mockProps";
5 | import { ControllerRollout } from "./ControllerRollout";
6 | describe("Controller Rollout", () => {
7 | it("should render", () => {
8 | const { getByText } = render(
9 |
12 |
13 |
14 | );
15 | expect(
16 | getByText("Controller Name : numaflow-controller")
17 | ).toBeInTheDocument();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/scheme/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | // This package contains the scheme of the automatically generated clientset.
19 | package scheme
20 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/fake/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | // Package fake has the automatically generated clients.
19 | package fake
20 |
--------------------------------------------------------------------------------
/config/kustomize/examples/transformer/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | configurations:
5 | - https://raw.githubusercontent.com/numaproj/numaplane/main/config/kustomize/numaplane-transformer-config.yaml
6 |
7 | namePrefix: my-
8 |
9 | resources:
10 | - my-pipelinerollout.yaml
11 | - my-isbservicerollout.yaml
12 | - my-monovertexrollout.yaml
13 |
14 | configMapGenerator:
15 | - name: my-cm
16 | literals:
17 | - FOO=BAR
18 |
19 | secretGenerator:
20 | - name: my-secret
21 | literals:
22 | - password=Pa5SW0rD
23 |
24 | commonLabels:
25 | foo: bar
26 |
27 | commonAnnotations:
28 | foo: bar
29 |
30 | images:
31 | - name: my-pipeline/my-udf
32 | newTag: my-version
33 |
34 | replicas:
35 | - name: simple-isbservice
36 | count: 3
37 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | // This package has the automatically generated typed clients.
19 | package v1alpha1
20 |
--------------------------------------------------------------------------------
/config/rbac/numaplane-aggregate-to-view.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | rbac.authorization.k8s.io/aggregate-to-view: "true"
6 | name: numaplane-aggregate-to-view
7 | rules:
8 | - apiGroups:
9 | - numaplane.numaproj.io
10 | resources:
11 | - pipelinerollouts
12 | - isbservicerollouts
13 | - numaflowcontrollerrollouts
14 | - numaflowcontrollers
15 | - monovertexrollouts
16 | verbs:
17 | - get
18 | - list
19 | - watch
20 | - apiGroups:
21 | - numaplane.numaproj.io
22 | resources:
23 | - pipelinerollouts/status
24 | - isbservicerollouts/status
25 | - numaflowcontrollerrollouts/status
26 | - numaflowcontrollers/status
27 | - monovertexrollouts/status
28 | verbs:
29 | - get
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - service_account.yaml
6 | - role.yaml
7 | - role_binding.yaml
8 | - leader_election_role.yaml
9 | - leader_election_role_binding.yaml
10 | - pipelinerollout_editor_role.yaml
11 | - pipelinerollout_viewer_role.yaml
12 | - numaflowcontrollerrollout_editor_role.yaml
13 | - numaflowcontrollerrollout_viewer_role.yaml
14 | - isbservicerollout_editor_role.yaml
15 | - isbservicerollout_viewer_role.yaml
16 | - monovertexrollout_editor_role.yaml
17 | - monovertexrollout_viewer_role.yaml
18 | - numaplane-aggregate-to-admin.yaml
19 | - numaplane-aggregate-to-edit.yaml
20 | - numaplane-aggregate-to-view.yaml
21 | - numaflowcontroller_editor_role.yaml
22 | - numaflowcontroller_viewer_role.yaml
23 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/RolloutComponentWrapper.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import { mockISBRolloutProps } from "../../mocks/mockProps";
3 | import {
4 | RolloutComponentContext,
5 | RolloutComponentWrapper,
6 | } from "./RolloutComponentWrapper";
7 |
8 | describe("RolloutComponentWrapper", () => {
9 | it("should render", () => {
10 | render(
11 |
17 | );
18 |
19 | const doc = screen.getByText("Actual Weight");
20 | expect(doc).toBeInTheDocument();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tests/manifests/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - ../../../config/default
6 | - ./controller_def_1.4.6.yaml
7 | - ./controller_def_1.5.2.yaml
8 | - ./controller_def_1.7.0.yaml
9 | - ./prometheus-monitors.yaml
10 | - https://raw.githubusercontent.com/kubernetes/autoscaler/vpa-release-1.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml
11 |
12 | patches:
13 | - patch: |-
14 | - op: add
15 | path: /spec/template/spec/containers/0/imagePullPolicy
16 | value: IfNotPresent
17 | target:
18 | kind: Deployment
19 | name: numaplane-controller-manager
20 |
21 | configMapGenerator:
22 | - name: numaplane-controller-config
23 | namespace: numaplane-system
24 | files:
25 | - config.yaml
26 | behavior: merge # Optional, defaults to "create"
27 |
--------------------------------------------------------------------------------
/config/rbac/numaplane-aggregate-to-edit.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | rbac.authorization.k8s.io/aggregate-to-edit: "true"
6 | name: numaplane-aggregate-to-edit
7 | rules:
8 | - apiGroups:
9 | - numaplane.numaproj.io
10 | resources:
11 | - pipelinerollouts
12 | - isbservicerollouts
13 | - numaflowcontrollerrollouts
14 | - numaflowcontrollers
15 | - monovertexrollouts
16 | verbs:
17 | - create
18 | - delete
19 | - get
20 | - list
21 | - patch
22 | - update
23 | - watch
24 | - apiGroups:
25 | - numaplane.numaproj.io
26 | resources:
27 | - pipelinerollouts/status
28 | - isbservicerollouts/status
29 | - numaflowcontrollerrollouts/status
30 | - numaflowcontrollers/status
31 | - monovertexrollouts/status
32 | verbs:
33 | - get
--------------------------------------------------------------------------------
/config/rbac/numaplane-aggregate-to-admin.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | labels:
5 | rbac.authorization.k8s.io/aggregate-to-admin: "true"
6 | name: numaplane-aggregate-to-admin
7 | rules:
8 | - apiGroups:
9 | - numaplane.numaproj.io
10 | resources:
11 | - pipelinerollouts
12 | - isbservicerollouts
13 | - numaflowcontrollerrollouts
14 | - numaflowcontrollers
15 | - monovertexrollouts
16 | verbs:
17 | - create
18 | - delete
19 | - get
20 | - list
21 | - patch
22 | - update
23 | - watch
24 | - apiGroups:
25 | - numaplane.numaproj.io
26 | resources:
27 | - pipelinerollouts/status
28 | - isbservicerollouts/status
29 | - numaflowcontrollerrollouts/status
30 | - numaflowcontrollers/status
31 | - monovertexrollouts/status
32 | verbs:
33 | - get
34 |
35 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: role
7 | app.kubernetes.io/component: rbac
8 | app.kubernetes.io/part-of: numaplane
9 | name: numaplane-leader-election-role
10 | rules:
11 | - apiGroups:
12 | - ""
13 | resources:
14 | - configmaps
15 | verbs:
16 | - get
17 | - list
18 | - watch
19 | - create
20 | - update
21 | - patch
22 | - delete
23 | - apiGroups:
24 | - coordination.k8s.io
25 | resources:
26 | - leases
27 | verbs:
28 | - get
29 | - list
30 | - watch
31 | - create
32 | - update
33 | - patch
34 | - delete
35 | - apiGroups:
36 | - ""
37 | resources:
38 | - events
39 | verbs:
40 | - create
41 | - patch
--------------------------------------------------------------------------------
/tests/config/testconfig.yaml:
--------------------------------------------------------------------------------
1 | logLevel: INFO # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
2 | numaflowControllerImageNames:
3 | - numaflow
4 | - numaflow-rc
5 | includedResources: "group=apps,kind=Deployment;group=,kind=ConfigMap;group=,kind=Secret;group=,kind=ServiceAccount;group=,kind=Namespace;group=rbac.authorization.k8s.io,kind=RoleBinding;group=rbac.authorization.k8s.io,kind=Role"
6 | defaultUpgradeStrategy: "pause-and-drain"
7 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=,kind=ConfigMap;group=autoscaling,kind=HorizontalPodAutoscaler"
8 | progressive:
9 | defaultAssessmentSchedule:
10 | - kind: Pipeline
11 | schedule: "120,360,0,10"
12 | - kind: MonoVertex
13 | schedule: "120,360,0,10"
14 | - kind: InterstepBufferService
15 | schedule: "0,480,0,10"
16 | analysisRunTimeout: "1200"
17 |
18 |
--------------------------------------------------------------------------------
/tests/manifests/default/config.yaml:
--------------------------------------------------------------------------------
1 | logLevel: DEBUG # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
2 | numaflowControllerImageNames:
3 | - numaflow
4 | - numaflow-rc
5 | includedResources: "group=apps,kind=Deployment;\
6 | group=,kind=ConfigMap;group=,kind=ServiceAccount;group=,kind=Secret;group=,kind=Service;\
7 | group=rbac.authorization.k8s.io,kind=RoleBinding;group=rbac.authorization.k8s.io,kind=Role"
8 | defaultUpgradeStrategy: "progressive"
9 | progressive:
10 | defaultAssessmentSchedule:
11 | - kind: Pipeline
12 | schedule: "10,180,60,10"
13 | - kind: MonoVertex
14 | schedule: "10,180,60,10"
15 | - kind: InterstepBufferService
16 | schedule: "0,0,0,10"
17 | analysisRunTimeout: "1200"
18 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=,kind=ConfigMap;group=autoscaling,kind=HorizontalPodAutoscaler"
19 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/utils/SquareCancelIcon.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Tooltip } from "@mui/material";
2 | import React from "react";
3 | import { CheckIconProps } from "./SquareCheckIcon";
4 |
5 | export const SquareCancelIcon = ({ tooltipTitle }: CheckIconProps) => {
6 | return (
7 |
8 |
24 |
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | labels: "bug"
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 |
13 | 1. ....
14 | 2. ....
15 | 3. ....
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Environment (please complete the following information):**
24 |
25 | - Kubernetes: [e.g. v1.18.6]
26 |
27 | **Additional context**
28 | Add any other context about the problem here.
29 |
30 | ---
31 |
32 |
33 |
34 | **Message from the maintainers**:
35 |
36 | Impacted by this bug? Give it a 👍. We often sort issues this way to know what to prioritize.
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/utils/SquareCheckIcon.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Tooltip } from "@mui/material";
2 | import React from "react";
3 |
4 | export interface CheckIconProps {
5 | tooltipTitle?: string;
6 | }
7 | export const SquareCheckIcon = ({ tooltipTitle }: CheckIconProps) => {
8 | return (
9 |
10 |
26 |
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/dist/resources/extension-Numarollout.js/extensions-Numarollout.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /**
2 | * @license React
3 | * react-is.production.min.js
4 | *
5 | * Copyright (c) Facebook, Inc. and its affiliates.
6 | *
7 | * This source code is licensed under the MIT license found in the
8 | * LICENSE file in the root directory of this source tree.
9 | */
10 |
11 | /**
12 | * @license React
13 | * react-jsx-runtime.production.min.js
14 | *
15 | * Copyright (c) Facebook, Inc. and its affiliates.
16 | *
17 | * This source code is licensed under the MIT license found in the
18 | * LICENSE file in the root directory of this source tree.
19 | */
20 |
21 | /** @license React v16.13.1
22 | * react-is.production.min.js
23 | *
24 | * Copyright (c) Facebook, Inc. and its affiliates.
25 | *
26 | * This source code is licensed under the MIT license found in the
27 | * LICENSE file in the root directory of this source tree.
28 | */
29 |
--------------------------------------------------------------------------------
/tests/manifests/e2e/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This snippet is used to remove the liveness and readiness probes as well as others from deployment, which is required to run the e2e tests.
2 | apiVersion: kustomize.config.k8s.io/v1beta1
3 | kind: Kustomization
4 |
5 | resources:
6 | - ../../default
7 |
8 | patches:
9 | - patch: |-
10 | - op: add
11 | path: /spec/template/spec/containers/0/imagePullPolicy
12 | value: IfNotPresent
13 | - op: remove
14 | path: /spec/template/spec/containers/0/livenessProbe
15 | value: null
16 | - op: remove
17 | path: /spec/template/spec/containers/0/readinessProbe
18 | value: null
19 | - op: remove
20 | path: /spec/template/spec/containers/0/command
21 | value: null
22 | - op: remove
23 | path: /spec/template/spec/containers/0/args
24 | value: null
25 | target:
26 | kind: Deployment
27 | name: numaplane-controller-manager
28 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/clientset.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/numaproj/numaplane/pkg/client/clientset/versioned"
7 | "k8s.io/client-go/dynamic"
8 | clientkube "k8s.io/client-go/kubernetes"
9 | "k8s.io/client-go/rest"
10 | )
11 |
12 | var DynamicClient *dynamic.DynamicClient
13 | var KubernetesClient *clientkube.Clientset
14 | var NumaplaneClient *versioned.Clientset
15 |
16 | func SetClientSets(restConfig *rest.Config) error {
17 | var err error
18 | DynamicClient, err = dynamic.NewForConfig(restConfig)
19 | if err != nil {
20 | return fmt.Errorf("failed to create dynamic client: %v", err)
21 | }
22 |
23 | KubernetesClient, err = clientkube.NewForConfig(restConfig)
24 | if err != nil {
25 | return fmt.Errorf("failed to create kubernetes clientset: %v", err)
26 | }
27 |
28 | NumaplaneClient, err = versioned.NewForConfig(restConfig)
29 | if err != nil {
30 | return fmt.Errorf("failed to create numaplane clientset: %v", err)
31 | }
32 |
33 | return nil
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/generated_expansion.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | type ISBServiceRolloutExpansion interface{}
21 |
22 | type MonoVertexRolloutExpansion interface{}
23 |
24 | type NumaflowControllerExpansion interface{}
25 |
26 | type NumaflowControllerRolloutExpansion interface{}
27 |
28 | type PipelineRolloutExpansion interface{}
29 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/utils/SquareCheckIcon.test.tsx:
--------------------------------------------------------------------------------
1 | // Test SquareCancelIcon component
2 |
3 | import React from "react";
4 | import { fireEvent, render, screen, waitFor } from "@testing-library/react";
5 | import { SquareCheckIcon } from "./SquareCheckIcon";
6 |
7 | describe("SquareCancelIcon", () => {
8 | it("should render the SquareCancelIcon component without errors", () => {
9 | const { container } = render();
10 | expect(container).toBeInTheDocument();
11 | });
12 | it("should render correctly with an empty tooltipTitle", async () => {
13 | const tooltipTitle = "test1";
14 | render();
15 | const icon = screen.getByTestId(`tooltip-${tooltipTitle}`);
16 | expect(icon).toBeInTheDocument();
17 | fireEvent.mouseOver(icon);
18 | await waitFor(() => {
19 | const tooltip = screen.getByText(`${tooltipTitle}`);
20 | expect(tooltip).toBeInTheDocument();
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/utils/SquareCancelIcon.test.tsx:
--------------------------------------------------------------------------------
1 | // Test SquareCancelIcon component
2 |
3 | import React from "react";
4 | import { fireEvent, render, screen, waitFor } from "@testing-library/react";
5 | import { SquareCancelIcon } from "./SquareCancelIcon";
6 |
7 | describe("SquareCancelIcon", () => {
8 | it("should render the SquareCancelIcon component without errors", () => {
9 | const { container } = render();
10 | expect(container).toBeInTheDocument();
11 | });
12 | it("should render correctly with an empty tooltipTitle", async () => {
13 | const tooltipTitle = "test1";
14 | render();
15 | const icon = screen.getByTestId(`tooltip-${tooltipTitle}`);
16 | expect(icon).toBeInTheDocument();
17 | fireEvent.mouseOver(icon);
18 | await waitFor(() => {
19 | const tooltip = screen.getByText(`${tooltipTitle}`);
20 | expect(tooltip).toBeInTheDocument();
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/structured_util_test.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | corev1 "k8s.io/api/core/v1"
9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 | "k8s.io/apimachinery/pkg/runtime"
11 | "sigs.k8s.io/controller-runtime/pkg/client/fake"
12 | )
13 |
14 | func TestGetSecret(t *testing.T) {
15 | scheme := runtime.NewScheme()
16 | err := corev1.AddToScheme(scheme)
17 | assert.NoError(t, err)
18 | fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
19 | &corev1.Secret{
20 | ObjectMeta: metav1.ObjectMeta{
21 | Name: "test-secret",
22 | Namespace: "test-namespace",
23 | },
24 | Data: map[string][]byte{
25 | "key": []byte("value"),
26 | },
27 | },
28 | ).Build()
29 |
30 | ctx := context.TODO()
31 |
32 | secret, err := GetSecret(ctx, fakeClient, "test-namespace", "test-secret")
33 |
34 | assert.NoError(t, err)
35 | assert.NotNil(t, secret)
36 | assert.Equal(t, "value", string(secret.Data["key"]))
37 | }
38 |
--------------------------------------------------------------------------------
/hack/update-codegen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o nounset
5 | set -o pipefail
6 |
7 | source $(dirname $0)/library.sh
8 | header "running codegen"
9 |
10 | ensure_vendor
11 |
12 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${REPO_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
13 |
14 | source "${CODEGEN_PKG}/kube_codegen.sh"
15 |
16 | THIS_PKG="github.com/numaproj/numaplane"
17 |
18 | subheader "running deepcopy gen"
19 |
20 | kube::codegen::gen_helpers \
21 | --boilerplate "${REPO_ROOT}/hack/boilerplate.go.txt" \
22 | "${REPO_ROOT}/pkg/apis"
23 |
24 | subheader "running clients gen"
25 |
26 | kube::codegen::gen_client \
27 | --with-watch \
28 | --output-dir "${REPO_ROOT}/pkg/client" \
29 | --output-pkg "${THIS_PKG}/pkg/client" \
30 | --boilerplate "${REPO_ROOT}/hack/boilerplate.go.txt" \
31 | --one-input-api "numaplane/v1alpha1" \
32 | "${REPO_ROOT}/pkg/apis"
33 |
34 | # gofmt the tree
35 | subheader "running gofmt"
36 | find . -name "*.go" -type f -print0 | xargs -0 gofmt -s -w
37 |
38 |
--------------------------------------------------------------------------------
/config/kustomize/examples/transformer/my-pipelinerollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: PipelineRollout
3 | metadata:
4 | name: simple-pipeline
5 | spec:
6 | pipeline:
7 | spec:
8 | vertices:
9 | - name: in
10 | source:
11 | generator:
12 | rpu: 5
13 | duration: 1s
14 | - name: my-udf
15 | udf:
16 | container:
17 | image: my-pipeline/my-udf:v0.1
18 | volumeMounts:
19 | - name: config-volume
20 | mountPath: /etc/config
21 | - name: secret-volume
22 | mountPath: /etc/secrets
23 | volumes:
24 | - name: config-volume
25 | configMap:
26 | name: my-cm
27 | - name: secret-volume
28 | secret:
29 | secretName: my-secret
30 | - name: out
31 | sink:
32 | log: {}
33 | edges:
34 | - from: in
35 | to: my-udf
36 | - from: my-udf
37 | to: out
38 |
--------------------------------------------------------------------------------
/config/manager/controller-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: numaplane-controller-config
5 | data:
6 | config.yaml: |
7 | # Supported log levels are: VERBOSE, DEBUG, INFO, WARN. ERROR, FATAL will be printed regardless of the log level
8 | logLevel: INFO
9 | includedResources: "group=apps,kind=Deployment;\
10 | group=,kind=ConfigMap;group=,kind=ServiceAccount;group=,kind=Secret;group=,kind=Service;\
11 | group=rbac.authorization.k8s.io,kind=RoleBinding;group=rbac.authorization.k8s.io,kind=Role"
12 | defaultUpgradeStrategy: "progressive"
13 | progressive:
14 | defaultAssessmentSchedule:
15 | - kind: Pipeline
16 | schedule: "10,200,60,10"
17 | - kind: MonoVertex
18 | schedule: "10,200,60,10"
19 | - kind: InterstepBufferService
20 | schedule: "0,0,0,10"
21 | analysisRunTimeout: 1200
22 | permittedRiders: "group=autoscaling.k8s.io,kind=VerticalPodAutoscaler;group=autoscaling,kind=HorizontalPodAutoscaler"
23 | pipeline:
24 | forceDrainFailureWaitDuration: 15
25 | maxRecyclableDurationMinutes: 240 # 4 hours
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutComponent.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import { CANARY } from "../../utils/Constants";
3 | import { ArgoRolloutComponent } from "./ArgoRolloutComponent";
4 | const rolloutParams = {
5 | strategy: CANARY,
6 | setWeight: "0",
7 | actualWeight: "100",
8 | step: "1",
9 | };
10 |
11 | //Mock the ArgoRolloutSummary component
12 | jest.mock("./ArgoRolloutSummary", () => {
13 | return {
14 | ArgoRolloutSummary: () => {
15 | return
ArgoRolloutSummary
;
16 | },
17 | };
18 | });
19 |
20 | //Mock the ArgoRolloutContainers component
21 | jest.mock("./ArgoRolloutContainers", () => {
22 | return {
23 | ArgoRolloutContainers: () => {
24 | return ArgoRolloutContainers
;
25 | },
26 | };
27 | });
28 |
29 | describe("ArgoRolloutComponent", () => {
30 | it("should render ArgoRolloutComponent", () => {
31 | render();
32 | expect(screen.getByText("ArgoRolloutSummary")).toBeInTheDocument();
33 | expect(screen.getByText("ArgoRolloutContainers")).toBeInTheDocument();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/internal/controller/config/options.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package config
18 |
19 | type options struct {
20 | configsPath string
21 | configFileName string
22 | fileType string
23 | }
24 |
25 | type Option func(*options)
26 |
27 | func defaultOptions() *options {
28 | return &options{
29 | fileType: "yaml",
30 | }
31 | }
32 |
33 | func WithConfigsPath(configsPath string) Option {
34 | return func(o *options) {
35 | o.configsPath = configsPath
36 | }
37 | }
38 |
39 | func WithConfigFileName(configFileName string) Option {
40 | return func(o *options) {
41 | o.configFileName = configFileName
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/utils/Constants.tsx:
--------------------------------------------------------------------------------
1 | import exp from "constants";
2 |
3 | export const HEALTHY = "HEALTHY";
4 | export const UNHEALTHY = "UNHEALTHY";
5 | export const PROGRESSING = "PROGRESSING";
6 | export const SUCCESSFUL = "SUCCESSFUL";
7 | export const RUNNING = "RUNNING";
8 | export const FAILURE = "FAILURE";
9 | export const DEGRADED = "DEGRADED";
10 | export const ERROR = "ERROR";
11 | export const POD = "Pod";
12 | export const REPLICA_SET = "ReplicaSet";
13 | export const ROLLOUT = "Rollout";
14 | export const CANARY = "Canary";
15 | export const REVISION = "REVISION";
16 | export const STATUS_REASON = "Status Reason";
17 | export const ANALYSIS_RUN = "AnalysisRun";
18 | export const INTUIT_DOMAIN = "intuit.com";
19 | export const ISB_SERVICE_ROLLOUT = "ISBServiceRollout";
20 | export const NUMAFLOW_CONTROLLER_ROLLOUT = "NumaflowControllerRollout";
21 | export const PIPELINE_ROLLOUT = "PipelineRollout";
22 | export const MONOVERTEX_ROLLOUT = "MonoVertexRollout";
23 | export const ISB_KUBERNETES = "isbsvc";
24 | export const CONTROLLER_MANAGER = "controller-manager";
25 | export const VERTEX = "vertex";
26 | export const MONO_VERTEX = "mono-vertex";
27 | export const KUBERNETES_APP = "app.kubernetes.io/component";
28 | export const ISB = "InterStepBufferService";
29 |
--------------------------------------------------------------------------------
/internal/util/queue.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "k8s.io/client-go/util/workqueue"
5 | )
6 |
7 | // rateLimitingQueue is a wrapper around RateLimitingInterface, which is essentially a queue that offers the following:
8 | // - if used correctly, only worker receives a given key at a time
9 | // - keys are rate limited to create fairness between keys
10 | // - keys can be scheduled to be re-processed at a certain time
11 | // TODO: Note that this wrapper doesn't provide any added benefit beyond the RateLimitingInterface that it wraps, but we can add
12 | // metrics for queue length to this by imitating this: https://github.com/argoproj/argo-workflows/blob/main/workflow/metrics/work_queue.go
13 | type rateLimitingQueue struct {
14 | workqueue.TypedRateLimitingInterface[interface{}]
15 | workerType string
16 | }
17 |
18 | func NewWorkQueue(queueName string) workqueue.TypedRateLimitingInterface[interface{}] {
19 | return rateLimitingQueue{
20 | TypedRateLimitingInterface: workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[interface{}]()),
21 | workerType: queueName,
22 | }
23 | }
24 |
25 | func (w rateLimitingQueue) Get() (interface{}, bool) {
26 | item, shutdown := w.TypedRateLimitingInterface.Get()
27 | return item, shutdown
28 | }
29 |
30 | func (w rateLimitingQueue) Done(item interface{}) {
31 | w.TypedRateLimitingInterface.Done(item)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/sync/testdata/pipeline-live.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaflow.numaproj.io/v1alpha1
2 | kind: Pipeline
3 | metadata:
4 | labels:
5 | numaplane.numaproj.io/tracking-id: my-example
6 | creationTimestamp: "2024-03-08T22:09:18Z"
7 | finalizers:
8 | - pipeline-controller
9 | generation: 3
10 | name: simple-pipeline
11 | namespace: numaflow-pipeline-example
12 | resourceVersion: "716917"
13 | uid: a38b5758-de8f-483a-9fc4-b46acb8f36c8
14 | spec:
15 | edges:
16 | - from: in
17 | to: cat
18 | - from: cat
19 | to: out
20 | lifecycle: {}
21 | vertices:
22 | - name: in
23 | source:
24 | generator:
25 | duration: 1s
26 | rpu: 5
27 | - name: cat
28 | udf:
29 | container:
30 | image: "quay.io/numaio/numaflow-go/map-cat:stable"
31 | imagePullPolicy: Always,
32 | - name: out
33 | sink:
34 | log: {}
35 | watermark: {}
36 | status:
37 | conditions:
38 | - lastTransitionTime: "2024-03-09T02:17:58Z"
39 | message: Successful
40 | reason: Successful
41 | status: "True"
42 | type: Configured
43 | - lastTransitionTime: "2024-03-09T02:17:58Z"
44 | message: Successful
45 | reason: Successful
46 | status: "True"
47 | type: Deployed
48 | lastUpdated: "2024-03-09T02:17:58Z"
49 | phase: Running
50 | sinkCount: 1
51 | sourceCount: 1
52 | udfCount: 1
53 | vertexCount: 3
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.23 as builder
3 | ARG TARGETOS
4 | ARG TARGETARCH
5 |
6 | WORKDIR /workspace
7 | # Copy the Go Modules manifests
8 | COPY go.mod go.mod
9 | COPY go.sum go.sum
10 | # cache deps before building and copying source so that we don't need to re-download as much
11 | # and so that source changes don't invalidate our downloaded layer
12 | RUN go mod download
13 |
14 | # Copy the go source
15 | COPY cmd/main.go cmd/main.go
16 | COPY pkg/ pkg/
17 | COPY internal/ internal/
18 |
19 | # Add a go build cache. The persistent cache helps speed up build steps,
20 | # especially steps that involve installing packages using a package manager.
21 | ENV GOCACHE=/root/.cache/go-build
22 | # Build
23 | # the GOARCH doesn't have a default value to allow the binary be built according to the host where the command
24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
27 | RUN --mount=type=cache,target="/root/.cache/go-build" CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -o manager cmd/main.go
28 |
29 | # Use alpine as minimal base image to package the manager binary
30 | FROM alpine
31 | WORKDIR /
32 | COPY --from=builder /workspace/manager .
33 |
34 | ENTRYPOINT ["/manager"]
35 |
--------------------------------------------------------------------------------
/PROJECT:
--------------------------------------------------------------------------------
1 | # Code generated by tool. DO NOT EDIT.
2 | # This file is used to track the info used to scaffold your project
3 | # and allow the plugins properly work.
4 | # More info: https://book.kubebuilder.io/reference/project-config.html
5 | domain: github.com.numaproj
6 | layout:
7 | - go.kubebuilder.io/v4
8 | projectName: numaplane
9 | repo: github.com/numaproj/numaplane
10 | resources:
11 | - api:
12 | crdVersion: v1alpha1
13 | namespaced: true
14 | controller: true
15 | domain: github.com.numaproj
16 | group: numaplane.numaproj.io
17 | kind: PipelineRollout
18 | path: github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1
19 | version: v1alpha1
20 | - api:
21 | crdVersion: v1alpha1
22 | namespaced: true
23 | controller: true
24 | domain: github.com.numaproj
25 | group: numaplane.numaproj.io
26 | kind: NumaflowControllerRollout
27 | path: github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1
28 | version: v1alpha1
29 | - api:
30 | crdVersion: v1alpha1
31 | namespaced: true
32 | controller: true
33 | domain: github.com.numaproj
34 | group: numaplane.numaproj.io
35 | kind: ISBServiceRollout
36 | path: github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1
37 | version: v1alpha1
38 | - api:
39 | crdVersion: v1alpha1
40 | namespaced: true
41 | controller: true
42 | domain: github.com.numaproj
43 | group: numaplane.numaproj.io
44 | kind: NumaflowController
45 | path: github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1
46 | version: v1alpha1
47 | version: "3"
48 |
--------------------------------------------------------------------------------
/hack/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 |
3 | /*
4 | Copyright 2023.
5 |
6 | Licensed under the Apache License, Version 2.0 (the "License");
7 | you may not use this file except in compliance with the License.
8 | You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing, software
13 | distributed under the License is distributed on an "AS IS" BASIS,
14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | See the License for the specific language governing permissions and
16 | limitations under the License.
17 | */
18 |
19 | // This package contains code generation utilities
20 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies
21 | package tools
22 |
23 | import (
24 | _ "github.com/ahmetb/gen-crd-api-reference-docs"
25 | _ "github.com/go-swagger/go-swagger/cmd/swagger"
26 | _ "github.com/gogo/protobuf/gogoproto"
27 | _ "github.com/gogo/protobuf/protoc-gen-gogo"
28 | _ "github.com/gogo/protobuf/protoc-gen-gogofast"
29 | _ "golang.org/x/tools/cmd/goimports"
30 | _ "k8s.io/code-generator"
31 | _ "k8s.io/code-generator/cmd/client-gen"
32 | _ "k8s.io/code-generator/cmd/deepcopy-gen"
33 | _ "k8s.io/code-generator/cmd/defaulter-gen"
34 | _ "k8s.io/code-generator/cmd/go-to-protobuf"
35 | _ "k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo"
36 | _ "k8s.io/code-generator/cmd/informer-gen"
37 | _ "k8s.io/code-generator/cmd/lister-gen"
38 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen"
39 | )
40 |
--------------------------------------------------------------------------------
/internal/util/logger/base_logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "strings"
5 | "sync"
6 |
7 | "github.com/numaproj/numaplane/internal/controller/config"
8 | )
9 |
10 | // Singleton Base Logger from which other loggers can be derived
11 | // maintains current log level for the application as a whole
12 |
13 | var (
14 | baseLogger *NumaLogger = New()
15 | baseLoggerMutex sync.RWMutex
16 | )
17 |
18 | // SetBaseLogger is intended to be set once when application starts
19 | func SetBaseLogger(nl *NumaLogger) {
20 | baseLoggerMutex.Lock()
21 | defer baseLoggerMutex.Unlock()
22 |
23 | baseLogger = nl.DeepCopy()
24 | // if the Global ConfigMap changes, update the BaseLogger's log level
25 | config.GetConfigManagerInstance().RegisterCallback(refreshBaseLoggerLevel)
26 | }
27 |
28 | // Get the standard NumaLogger with current Log Level - deep copy it in case user modifies it
29 | func GetBaseLogger() *NumaLogger {
30 | baseLoggerMutex.RLock()
31 | defer baseLoggerMutex.RUnlock()
32 | return baseLogger.DeepCopy()
33 | }
34 |
35 | // Refresh the Logger's LogLevel based on current config value
36 | func refreshBaseLoggerLevel(newConfig config.GlobalConfig) {
37 |
38 | // if it changed, propagate it to our Base Logger
39 | if LogLevelMap[strings.ToUpper(newConfig.LogLevel)] != baseLogger.LogLevel {
40 |
41 | baseLoggerMutex.Lock()
42 | defer baseLoggerMutex.Unlock()
43 |
44 | // update the logger with the new log level
45 | baseLogger.SetLevel(LogLevelMap[strings.ToUpper(newConfig.LogLevel)])
46 | baseLogger.Infof("updated log level=%s", newConfig.LogLevel)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by informer-gen. DO NOT EDIT.
17 |
18 | package internalinterfaces
19 |
20 | import (
21 | time "time"
22 |
23 | versioned "github.com/numaproj/numaplane/pkg/client/clientset/versioned"
24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 | runtime "k8s.io/apimachinery/pkg/runtime"
26 | cache "k8s.io/client-go/tools/cache"
27 | )
28 |
29 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
30 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
31 |
32 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle
33 | type SharedInformerFactory interface {
34 | Start(stopCh <-chan struct{})
35 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
36 | }
37 |
38 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions.
39 | type TweakListOptionsFunc func(*v1.ListOptions)
40 |
--------------------------------------------------------------------------------
/internal/controller/common/rollout_object.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package common
18 |
19 | import (
20 | "fmt"
21 | "strconv"
22 | "strings"
23 |
24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 | "k8s.io/apimachinery/pkg/runtime/schema"
26 |
27 | apiv1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
28 | )
29 |
30 | type RolloutObject interface {
31 | GetRolloutGVR() metav1.GroupVersionResource
32 |
33 | GetRolloutGVK() schema.GroupVersionKind
34 |
35 | GetChildGVR() metav1.GroupVersionResource
36 |
37 | GetChildGVK() schema.GroupVersionKind
38 |
39 | GetRolloutObjectMeta() *metav1.ObjectMeta
40 |
41 | GetRolloutStatus() *apiv1.Status
42 | }
43 |
44 | // assume child name is "-"
45 | func GetRolloutParentName(childName string) (string, error) {
46 |
47 | index := strings.LastIndex(childName, "-")
48 | if index > 0 && index < len(childName)-1 {
49 | _, err := strconv.Atoi(childName[index+1:])
50 | if err == nil {
51 | return childName[:index], nil
52 | }
53 | }
54 | return "", fmt.Errorf("unexpected child name %q doesn't end with '-'", childName)
55 | }
56 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutContainers.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import { ArgoRolloutContainers } from "./ArgoRolloutContainers";
3 | import { RolloutComponentContext } from "../RolloutComponentWrapper";
4 | import {
5 | mockControllerRolloutProps,
6 | mockISBRolloutProps,
7 | mockPipelineRolloutProps,
8 | } from "../../../mocks/mockProps";
9 |
10 | describe("ArgoRolloutContainers", () => {
11 | it("should render for isb rollout", () => {
12 | render(
13 |
14 |
15 |
16 | );
17 | expect(
18 | screen.getByDisplayValue("docker.intuit.com/docker-rmt/nats:2.10.11")
19 | ).toBeInTheDocument();
20 | });
21 |
22 | it("should render for controller rollout", () => {
23 | render(
24 |
27 |
28 |
29 | );
30 | expect(
31 | screen.getByDisplayValue(
32 | "docker.intuit.com/quay-rmt/numaproj/numaflow:v1.2.1"
33 | )
34 | ).toBeInTheDocument();
35 | });
36 |
37 | it("should render for pipeline rollout", () => {
38 | render(
39 |
40 |
41 |
42 | );
43 | expect(
44 | screen.getByDisplayValue(
45 | "docker.intuit.com/quay-rmt/numaproj/numaflow:v1.2.1"
46 | )
47 | ).toBeInTheDocument();
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/tests/manifests/default/prometheus-monitors.yaml:
--------------------------------------------------------------------------------
1 | # These are used by the Prometheus Operator so it can query Numaflow Pipeline and MonoVertex Pods for metrics
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: ServiceMonitor
4 | metadata:
5 | labels:
6 | app.kubernetes.io/part-of: numaflow
7 | name: numaflow-pipeline-metrics
8 | spec:
9 | namespaceSelector:
10 | any: true
11 | endpoints:
12 | - scheme: https
13 | port: metrics
14 | targetPort: 2469
15 | tlsConfig:
16 | insecureSkipVerify: true
17 | selector:
18 | matchLabels:
19 | app.kubernetes.io/component: vertex
20 | app.kubernetes.io/managed-by: vertex-controller
21 | app.kubernetes.io/part-of: numaflow
22 | matchExpressions:
23 | - key: numaflow.numaproj.io/pipeline-name
24 | operator: Exists
25 | - key: numaflow.numaproj.io/vertex-name
26 | operator: Exists
27 | ---
28 | apiVersion: monitoring.coreos.com/v1
29 | kind: PodMonitor
30 | metadata:
31 | labels:
32 | app.kubernetes.io/part-of: numaflow
33 | name: numaflow-mono-vertex-metrics
34 | spec:
35 | namespaceSelector:
36 | any: true
37 | podMetricsEndpoints:
38 | - scheme: https
39 | path: /metrics
40 | port: metrics
41 | tlsConfig:
42 | insecureSkipVerify: true
43 | selector:
44 | matchExpressions:
45 | - key: app.kubernetes.io/part-of
46 | operator: In
47 | values:
48 | - numaflow
49 | - key: app.kubernetes.io/component
50 | operator: In
51 | values:
52 | - mono-vertex
53 | - key: app.kubernetes.io/managed-by
54 | operator: In
55 | values:
56 | - mono-vertex-controller
57 | - key: numaflow.numaproj.io/mono-vertex-name
58 | operator: Exists
59 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/PipelineRollout.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
3 | import { Box } from "@mui/material";
4 | import { SquareCheckIcon } from "../utils/SquareCheckIcon";
5 | import { SquareCancelIcon } from "../utils/SquareCancelIcon";
6 |
7 | export const PipelineRollout = () => {
8 | const { props, kindToNodeMap } = useContext(RolloutComponentContext);
9 | const conditions = props?.resource?.status?.conditions
10 | const hasChildResourcesHealthy = conditions.some(condition => condition.type === 'ChildResourcesHealthy');
11 |
12 | return (
13 |
14 |
15 | {kindToNodeMap.get("Pipeline")?.map((node) => {
16 | return (
17 |
18 | Pipeline Name: {node?.name}
19 |
32 | Pipeline Status:{" "}
33 | {hasChildResourcesHealthy ? (
34 |
35 | ) : (
36 |
37 | )}
38 |
39 |
40 | );
41 | })}
42 |
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/hack/library.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | readonly REPO_ROOT="$(git rev-parse --show-toplevel)"
4 |
5 | # Display a box banner.
6 | # Parameters: $1 - character to use for the box.
7 | # $2 - banner message.
8 | function make_banner() {
9 | local msg="$1$1$1$1 $2 $1$1$1$1"
10 | local border="${msg//[-0-9A-Za-z _.,:\/()]/$1}"
11 | echo -e "${border}\n${msg}\n${border}"
12 | }
13 |
14 | # Simple header for logging purposes.
15 | function header() {
16 | local upper="$(echo $1 | tr a-z A-Z)"
17 | make_banner "+" "${upper}"
18 | }
19 |
20 | # Simple subheader for logging purposes.
21 | function subheader() {
22 | make_banner "-" "$1"
23 | }
24 |
25 | # Simple warning banner for logging purposes.
26 | function warning() {
27 | make_banner "!" "$1"
28 | }
29 |
30 | function make_fake_paths() {
31 | FAKE_GOPATH="$(mktemp -d)"
32 | trap 'rm -rf ${FAKE_GOPATH}' EXIT
33 | FAKE_REPOPATH="${FAKE_GOPATH}/src/github.com/numaproj/numaplane"
34 | mkdir -p "$(dirname "${FAKE_REPOPATH}")" && ln -s "${REPO_ROOT}" "${FAKE_REPOPATH}"
35 | }
36 |
37 | ensure_vendor() {
38 | go mod vendor
39 | }
40 |
41 | ensure_pandoc() {
42 | if [ "`command -v pandoc`" = "" ]; then
43 | warning "Please install pandoc with - brew install pandoc"
44 | exit 1
45 | fi
46 | }
47 |
48 | ensure_protobuf() {
49 | if [ "`command -v protoc`" = "" ]; then
50 | warning "Please install protobuf with - brew install protobuf"
51 | exit 1
52 | fi
53 | }
54 |
55 | ensure_node(){
56 | if [ "`command -v node`" = "" ]; then
57 | warning "Please install node with - brew install node"
58 | exit 1
59 | fi
60 | }
61 |
62 | ensure_yarn(){
63 | if [ "`command -v yarn`" = "" ]; then
64 | warning "Please install yarn with - brew install yarn"
65 | exit 1
66 | fi
67 | }
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/MonovertexRollout.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
3 | import { Box } from "@mui/material";
4 | import { SquareCheckIcon } from "../utils/SquareCheckIcon";
5 | import { SquareCancelIcon } from "../utils/SquareCancelIcon";
6 |
7 | export const MonovertexRollout = () => {
8 | const { props, kindToNodeMap } = useContext(RolloutComponentContext);
9 | const conditions = props?.resource?.status?.conditions
10 | const hasChildResourcesHealthy = conditions.some(condition => condition.type === 'ChildResourcesHealthy');
11 |
12 | return (
13 |
14 |
15 | {kindToNodeMap.get("MonoVertexRollout")?.map((node) => {
16 | return (
17 |
18 | Monovertex Rollout Name: {node?.name}
19 |
32 | Monovertex Status:{" "}
33 | {hasChildResourcesHealthy ? (
34 |
35 | ) : (
36 |
37 | )}
38 |
39 |
40 | );
41 | })}
42 |
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/internal/controller/common/rollout_object_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package common
18 |
19 | import (
20 | "fmt"
21 | "testing"
22 | )
23 |
24 | func TestGetRolloutParentName(t *testing.T) {
25 | tests := []struct {
26 | childName string
27 | expectedParent string
28 | expectError bool
29 | }{
30 | {"parent-123", "parent", false}, // Valid case
31 | {"parent-child", "", true}, // Invalid case: no number
32 | {"parent-", "", true}, // Edge case: hyphen without number
33 | {"parent-0", "parent", false}, // Valid case: zero as number
34 | {"parent-abc", "", true}, // Invalid case: non-numeric suffix
35 | {"-123", "", true}, // Invalid case: no parent name
36 | {"parent-123-extra", "", true}, // Invalid case: extra suffix
37 | }
38 |
39 | for _, tt := range tests {
40 | t.Run(fmt.Sprintf("childName=%s", tt.childName), func(t *testing.T) {
41 | parentName, err := GetRolloutParentName(tt.childName)
42 | if (err != nil) != tt.expectError {
43 | t.Errorf("expected error: %v, got: %v", tt.expectError, err)
44 | }
45 | if parentName != tt.expectedParent {
46 | t.Errorf("expected parent name: %q, got: %q", tt.expectedParent, parentName)
47 | }
48 | })
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/client/informers/externalversions/numaplane/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by informer-gen. DO NOT EDIT.
17 |
18 | package numaplane
19 |
20 | import (
21 | internalinterfaces "github.com/numaproj/numaplane/pkg/client/informers/externalversions/internalinterfaces"
22 | v1alpha1 "github.com/numaproj/numaplane/pkg/client/informers/externalversions/numaplane/v1alpha1"
23 | )
24 |
25 | // Interface provides access to each of this group's versions.
26 | type Interface interface {
27 | // V1alpha1 provides access to shared informers for resources in V1alpha1.
28 | V1alpha1() v1alpha1.Interface
29 | }
30 |
31 | type group struct {
32 | factory internalinterfaces.SharedInformerFactory
33 | namespace string
34 | tweakListOptions internalinterfaces.TweakListOptionsFunc
35 | }
36 |
37 | // New returns a new Interface.
38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
39 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
40 | }
41 |
42 | // V1alpha1 returns a new v1alpha1.Interface.
43 | func (g *group) V1alpha1() v1alpha1.Interface {
44 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
45 | }
46 |
--------------------------------------------------------------------------------
/internal/controller/common/numaflowtypes/vertex.go:
--------------------------------------------------------------------------------
1 | package numaflowtypes
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/numaproj/numaplane/internal/util"
7 | apiv1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
8 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9 | )
10 |
11 | // AbstractVertex keeps track of minimum number of fields we need to know about in Numaflow's AbstractVertex, which are presumed not to change from version to version
12 | type AbstractVertex struct {
13 | Name string `json:"name"`
14 | Scale Scale `json:"scale,omitempty"`
15 | }
16 |
17 | // Scale keeps track of minimum number of fields we need to know about in Numaflow's Scale struct, which are presumed not to change from version to version
18 | type Scale struct {
19 | // Minimum replicas.
20 | Min *int32 `json:"min,omitempty"`
21 | // Maximum replicas.
22 | Max *int32 `json:"max,omitempty"`
23 | }
24 |
25 | func ExtractScaleMinMax(object map[string]any, pathToScale []string) (*apiv1.ScaleDefinition, error) {
26 |
27 | scaleDef, foundScale, err := unstructured.NestedMap(object, pathToScale...)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | if !foundScale {
33 | return nil, nil
34 | }
35 | scaleMinMax := apiv1.ScaleDefinition{}
36 | minInterface := scaleDef["min"]
37 | maxInterface := scaleDef["max"]
38 | if minInterface != nil {
39 | min, valid := util.ToInt64(minInterface)
40 | if !valid {
41 | return nil, fmt.Errorf("scale min %+v of unexpected type", minInterface)
42 | }
43 | scaleMinMax.Min = &min
44 | }
45 | if maxInterface != nil {
46 | max, valid := util.ToInt64(maxInterface)
47 | if !valid {
48 | return nil, fmt.Errorf("scale max %+v of unexpected type", maxInterface)
49 | }
50 | scaleMinMax.Max = &max
51 | }
52 | disabledInterface := scaleDef["disabled"]
53 | if disabledInterface != nil && disabledInterface.(bool) {
54 | scaleMinMax.Disabled = true
55 | }
56 |
57 | return &scaleMinMax, nil
58 | }
59 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { ArgoPropType } from "./ArgoPropType";
3 | import { RolloutComponentWrapper } from "./rollout/RolloutComponentWrapper";
4 |
5 | export const roundNumber = (num: number, dig: number): number => {
6 | return Math.round(num * 10 ** dig) / 10 ** dig;
7 | };
8 |
9 | export const Extension = (props: ArgoPropType) => {
10 | return (
11 |
12 |
17 |
18 | );
19 | };
20 |
21 | export const component = Extension;
22 |
23 | // Register the component extension in ArgoCD
24 |
25 | // registerResourceExtension(component: ExtensionComponent, group: string, kind: string, tabTitle: string)
26 | ((window: any) => {
27 | window?.extensionsAPI?.registerResourceExtension(
28 | component,
29 | "numaplane.numaproj.io",
30 | "ISBServiceRollout",
31 | "Numarollout",
32 | { icon: "fa fa-window-restore" }
33 | );
34 | window?.extensionsAPI?.registerResourceExtension(
35 | component,
36 | "numaplane.numaproj.io",
37 | "NumaflowControllerRollout",
38 | "Numarollout",
39 | { icon: "fa fa-window-restore" }
40 | );
41 | window?.extensionsAPI?.registerResourceExtension(
42 | component,
43 | "numaplane.numaproj.io",
44 | "PipelineRollout",
45 | "Numarollout",
46 | { icon: "fa fa-window-restore" }
47 | );
48 | window?.extensionsAPI?.registerResourceExtension(
49 | component,
50 | "numaplane.numaproj.io",
51 | "MonoVertexRollout",
52 | "Numarollout",
53 | { icon: "fa fa-window-restore" }
54 | );
55 | window?.extensionsAPI?.registerResourceExtension(
56 | component,
57 | "*",
58 | "Deployment",
59 | "Numarollout",
60 | { icon: "fa fa-window-restore" }
61 | );
62 | })(window);
63 |
--------------------------------------------------------------------------------
/config/rbac/role.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: numaplane-role
6 | rules:
7 | - apiGroups: ["numaflow.numaproj.io"]
8 | resources: ["*"]
9 | verbs: ["*"]
10 | - apiGroups: ["numaplane.numaproj.io"]
11 | resources: ["*"]
12 | verbs: ["*"]
13 | - apiGroups: [""]
14 | resources:
15 | - configmaps
16 | - serviceaccounts
17 | - secrets
18 | - services
19 | verbs:
20 | - '*'
21 | - apiGroups: ["rbac.authorization.k8s.io"]
22 | resources:
23 | - rolebindings
24 | - roles
25 | verbs:
26 | - '*'
27 | - apiGroups:
28 | - ""
29 | resources:
30 | - namespaces
31 | verbs:
32 | - 'get'
33 | - 'list'
34 | - 'watch'
35 | - apiGroups: ["apps"]
36 | resources:
37 | - deployments
38 | verbs:
39 | - '*'
40 | - apiGroups: ["apps"]
41 | resources:
42 | - statefulsets
43 | verbs:
44 | - 'get'
45 | - 'list'
46 | - 'watch'
47 | - apiGroups: ["policy"]
48 | resources: ["poddisruptionbudgets"]
49 | verbs: ["create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"]
50 | - apiGroups:
51 | - ""
52 | resources:
53 | - events
54 | verbs:
55 | - 'create'
56 | - 'patch'
57 | - apiGroups:
58 | - ""
59 | resources:
60 | - pods
61 | verbs:
62 | - 'list'
63 | - apiGroups: ["argoproj.io"]
64 | resources:
65 | - analysisruns
66 | verbs: ["*"]
67 | - apiGroups: ["argoproj.io"]
68 | resources:
69 | - analysistemplates
70 | - clusteranalysistemplates
71 | verbs:
72 | - 'get'
73 | - 'list'
74 | - 'watch'
75 | - apiGroups: ["autoscaling.k8s.io"]
76 | resources:
77 | - verticalpodautoscalers
78 | verbs: ["*"]
79 | - apiGroups: ["autoscaling"]
80 | resources:
81 | - horizontalpodautoscalers
82 | verbs: ["*"]
83 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 | on:
3 | push:
4 | tags:
5 | - 'v*'
6 | branches:
7 | - main
8 | - dev-*
9 |
10 | defaults:
11 | run:
12 | shell: bash
13 |
14 | jobs:
15 | build-push-linux-multi:
16 | name: Build & push linux/amd64 and linux/arm64
17 | runs-on: ubuntu-latest
18 | if: github.repository == 'numaproj/numaplane'
19 | strategy:
20 | matrix:
21 | target: [ numaplane ]
22 | steps:
23 | - uses: actions/checkout@v4
24 | - name: Set up Docker Buildx
25 | uses: docker/setup-buildx-action@v2
26 |
27 | - name: Registry Login
28 | uses: docker/login-action@v2
29 | with:
30 | registry: quay.io
31 | username: ${{ secrets.QUAYIO_USERNAME }}
32 | password: ${{ secrets.QUAYIO_PASSWORD }}
33 |
34 | - name: set Version
35 | id: version
36 | run: |
37 | tag=$(basename $GITHUB_REF)
38 | if [ $tag = "main" ]; then
39 | tag="latest"
40 | fi
41 | echo "VERSION=$tag" >> $GITHUB_OUTPUT
42 | - name: Container build and push with arm64/amd64
43 | run: make image docker-push IMAGE_FULL_PATH=${{ secrets.QUAYIO_ORG }}/numaplane-controller:${{ steps.version.outputs.VERSION }} CONTAINER_TOOL=docker
44 |
45 | Release:
46 | runs-on: ubuntu-latest
47 | if: github.repository == 'numaproj/numaplane'
48 | needs: [ build-push-linux-multi ]
49 | steps:
50 | - name: Checkout
51 | uses: actions/checkout@v4
52 | - name: Registry Login
53 | uses: docker/login-action@v2
54 | with:
55 | registry: quay.io
56 | username: ${{ secrets.QUAYIO_USERNAME }}
57 | password: ${{ secrets.QUAYIO_PASSWORD }}
58 | - name: Create Release
59 | uses: softprops/action-gh-release@v1
60 | if: startsWith(github.ref, 'refs/tags/')
61 | env:
62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63 |
--------------------------------------------------------------------------------
/tests/e2e/coverage/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.23 as builder
3 | ARG TARGETOS
4 | ARG TARGETARCH
5 |
6 | WORKDIR github.com/numaproj/numaplane
7 | # Copy the Go Modules manifests
8 | COPY go.mod go.mod
9 | COPY go.sum go.sum
10 | # cache deps before building and copying source so that we don't need to re-download as much
11 | # and so that source changes don't invalidate our downloaded layer
12 | RUN go mod download
13 |
14 | # Copy the go source
15 | COPY cmd/main.go cmd/main.go
16 | COPY pkg/ pkg/
17 | COPY internal/ internal/
18 |
19 | # Add a go build cache. The persistent cache helps speed up build steps,
20 | # especially steps that involve installing packages using a package manager.
21 | ENV GOCACHE=/root/.cache/go-build
22 | # Build
23 | # the GOARCH doesn't have a default value to allow the binary be built according to the host where the command
24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
27 | RUN --mount=type=cache,target="/root/.cache/go-build" CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -cover -covermode=atomic -coverpkg=./... -o manager cmd/main.go
28 |
29 | ## Use golang as image to package the manager binary
30 | ## This image will be used to run the e2e tests and generate the coverage report
31 | FROM golang:1.23
32 | WORKDIR /
33 | COPY --from=builder /go/github.com/numaproj/numaplane/manager .
34 | # Setup coverage directory to store the coverage report generated by while running the e2e tests
35 | RUN mkdir coverage
36 | ENV GOCOVERDIR=coverage
37 |
38 | # Copy entrypoint script which will be used to run the numaplane process in background
39 | COPY tests/e2e/coverage/entrypoint.sh /entrypoint.sh
40 | RUN chmod +x /entrypoint.sh
41 | ENTRYPOINT ["/entrypoint.sh"]
42 |
--------------------------------------------------------------------------------
/internal/controller/config/user_config.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package config
18 |
19 | import (
20 | "encoding/json"
21 | "fmt"
22 | "strings"
23 | )
24 |
25 | type USDEUserStrategy string
26 |
27 | const (
28 | ProgressiveStrategyID USDEUserStrategy = "progressive"
29 | PPNDStrategyID USDEUserStrategy = "pause-and-drain"
30 | NoStrategyID USDEUserStrategy = "no-strategy"
31 | )
32 |
33 | func (s *USDEUserStrategy) UnmarshalJSON(data []byte) (err error) {
34 | var usdeUserStrategyStr string
35 | if err := json.Unmarshal(data, &usdeUserStrategyStr); err != nil {
36 | return err
37 | }
38 |
39 | allowedValues := map[USDEUserStrategy]struct{}{
40 | ProgressiveStrategyID: {},
41 | PPNDStrategyID: {},
42 | NoStrategyID: {}}
43 |
44 | if strings.TrimSpace(usdeUserStrategyStr) == "" {
45 | usdeUserStrategyStr = string(NoStrategyID)
46 | }
47 |
48 | // Make sure the string is one of the possible strategy values
49 | _, found := allowedValues[USDEUserStrategy(usdeUserStrategyStr)]
50 | if !found {
51 | return fmt.Errorf("invalid strategy '%s' (allowed values: %+v)", usdeUserStrategyStr, allowedValues)
52 | }
53 |
54 | *s = USDEUserStrategy(usdeUserStrategyStr)
55 |
56 | return nil
57 | }
58 |
59 | func (s USDEUserStrategy) IsValid() bool {
60 | switch s {
61 | case ProgressiveStrategyID, PPNDStrategyID, NoStrategyID:
62 | return true
63 | default:
64 | return false
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/fake/register.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package fake
19 |
20 | import (
21 | numaplanev1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23 | runtime "k8s.io/apimachinery/pkg/runtime"
24 | schema "k8s.io/apimachinery/pkg/runtime/schema"
25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer"
26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
27 | )
28 |
29 | var scheme = runtime.NewScheme()
30 | var codecs = serializer.NewCodecFactory(scheme)
31 |
32 | var localSchemeBuilder = runtime.SchemeBuilder{
33 | numaplanev1alpha1.AddToScheme,
34 | }
35 |
36 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition
37 | // of clientsets, like in:
38 | //
39 | // import (
40 | // "k8s.io/client-go/kubernetes"
41 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme"
42 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
43 | // )
44 | //
45 | // kclientset, _ := kubernetes.NewForConfig(c)
46 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
47 | //
48 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
49 | // correctly.
50 | var AddToScheme = localSchemeBuilder.AddToScheme
51 |
52 | func init() {
53 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
54 | utilruntime.Must(AddToScheme(scheme))
55 | }
56 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutComponent.css:
--------------------------------------------------------------------------------
1 | .info__title {
2 | font-size: 18px;
3 | font-weight: 600;
4 | margin-bottom: .5em;
5 | }
6 | rollout__row .info {
7 | height: auto;
8 | }
9 | .rollout__info {
10 | width: 400px;
11 | margin-right: 15px;
12 | }
13 | .info {
14 | border-radius: 5px;
15 | padding: 15px;
16 | background-color: #fff;
17 | box-sizing: border-box;
18 | margin-left: 1rem;
19 | margin-bottom: 2rem;
20 | }
21 | .info-item--row {
22 | display: flex;
23 | align-items: center;
24 | flex-grow: 1;
25 | }
26 | .info-item--row label {
27 | margin-right: auto;
28 | padding-right: 5px;
29 | }
30 |
31 | .info-item--row__container {
32 | margin-left: auto;
33 | display: flex;
34 | min-width: 0;
35 | padding-left: 25px;
36 | flex-wrap: wrap;
37 | justify-content: flex-end;
38 | }
39 | .info-item--canary {
40 | background-color: #e4aa37;
41 | border: 1px solid #e4aa37;
42 | color: #09090f;
43 | }
44 | .info-item--row .info-item {
45 | margin: .25em 0;
46 | margin-left: 5px;
47 | }
48 | .ant-input {
49 | box-sizing: border-box;
50 | margin: 0;
51 | padding: 4px 11px;
52 | color: rgba(0, 0, 0, 0.88);
53 | font-size: 14px;
54 | line-height: 1.5714285714285714;
55 | list-style: none;
56 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
57 | position: relative;
58 | display: inline-block;
59 | width: 100%;
60 | min-width: 0;
61 | background-color: #ffffff;
62 | background-image: none;
63 | border-width: 1px;
64 | border-style: solid;
65 | border-color: #d9d9d9;
66 | border-radius: 6px;
67 | transition: all 0.2s;
68 | }
69 | .ant-input[disabled] {
70 | color: rgba(0, 0, 0, 0.25);
71 | background-color: rgba(0, 0, 0, 0.04);
72 | border-color: #d9d9d9;
73 | box-shadow: none;
74 | cursor: not-allowed;
75 | opacity: 1;
76 | }
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | // What are the options for groupKind
4 | const extName = "Numarollout";
5 |
6 | const config = {
7 | mode: "production",
8 | entry: {
9 | extension: "./src/index.tsx",
10 | },
11 | output: {
12 | filename: `extensions-${extName}.js`,
13 | path: __dirname + `/dist/resources/extension-${extName}.js`,
14 | libraryTarget: "window",
15 | library: ["tmp", "extensions"],
16 | },
17 | resolve: {
18 | extensions: [".ts", ".tsx", ".js", ".json", ".ttf"],
19 | fallback: {
20 | url: require.resolve("url/"),
21 | // You can add other polyfills if needed
22 | },
23 | },
24 | externals: {
25 | react: "React",
26 | "react-dom": "ReactDOM",
27 | moment: "Moment",
28 | },
29 | optimization: {
30 | minimize: true,
31 | },
32 | module: {
33 | rules: [
34 | {
35 | test: /\.(ts|js)x?$/,
36 | exclude: /node_modules/,
37 | use: [
38 | {
39 | loader: "ts-loader",
40 | options: {
41 | // specify TypeScript compiler options
42 | compilerOptions: {
43 | target: "es5",
44 | },
45 | appendTsSuffixTo: [/\.vue$/],
46 | appendTsxSuffixTo: [/\.vue$/],
47 | transpileOnly: true,
48 | },
49 | },
50 | ],
51 | },
52 | {
53 | test: /\.tsx?$/,
54 | include: [/argo-rollouts\/ui/, /argo-ui/], // Add argo-ui package here
55 | use: [
56 | {
57 | loader: "ts-loader",
58 | options: {
59 | transpileOnly: true,
60 | },
61 | },
62 | ],
63 | },
64 | {
65 | test: /\.scss$/,
66 | use: ["style-loader", "raw-loader", "sass-loader"],
67 | },
68 | {
69 | test: /\.css$/,
70 | use: ["style-loader", "raw-loader"],
71 | },
72 | ],
73 | },
74 | };
75 |
76 | module.exports = config;
77 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/fake/fake_numaplane_client.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package fake
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/client/clientset/versioned/typed/numaplane/v1alpha1"
22 | rest "k8s.io/client-go/rest"
23 | testing "k8s.io/client-go/testing"
24 | )
25 |
26 | type FakeNumaplaneV1alpha1 struct {
27 | *testing.Fake
28 | }
29 |
30 | func (c *FakeNumaplaneV1alpha1) ISBServiceRollouts(namespace string) v1alpha1.ISBServiceRolloutInterface {
31 | return &FakeISBServiceRollouts{c, namespace}
32 | }
33 |
34 | func (c *FakeNumaplaneV1alpha1) MonoVertexRollouts(namespace string) v1alpha1.MonoVertexRolloutInterface {
35 | return &FakeMonoVertexRollouts{c, namespace}
36 | }
37 |
38 | func (c *FakeNumaplaneV1alpha1) NumaflowControllers(namespace string) v1alpha1.NumaflowControllerInterface {
39 | return &FakeNumaflowControllers{c, namespace}
40 | }
41 |
42 | func (c *FakeNumaplaneV1alpha1) NumaflowControllerRollouts(namespace string) v1alpha1.NumaflowControllerRolloutInterface {
43 | return &FakeNumaflowControllerRollouts{c, namespace}
44 | }
45 |
46 | func (c *FakeNumaplaneV1alpha1) PipelineRollouts(namespace string) v1alpha1.PipelineRolloutInterface {
47 | return &FakePipelineRollouts{c, namespace}
48 | }
49 |
50 | // RESTClient returns a RESTClient that is used to communicate
51 | // with API server by this client implementation.
52 | func (c *FakeNumaplaneV1alpha1) RESTClient() rest.Interface {
53 | var ret *rest.RESTClient
54 | return ret
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/scheme/register.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package scheme
19 |
20 | import (
21 | numaplanev1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23 | runtime "k8s.io/apimachinery/pkg/runtime"
24 | schema "k8s.io/apimachinery/pkg/runtime/schema"
25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer"
26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
27 | )
28 |
29 | var Scheme = runtime.NewScheme()
30 | var Codecs = serializer.NewCodecFactory(Scheme)
31 | var ParameterCodec = runtime.NewParameterCodec(Scheme)
32 | var localSchemeBuilder = runtime.SchemeBuilder{
33 | numaplanev1alpha1.AddToScheme,
34 | }
35 |
36 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition
37 | // of clientsets, like in:
38 | //
39 | // import (
40 | // "k8s.io/client-go/kubernetes"
41 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme"
42 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
43 | // )
44 | //
45 | // kclientset, _ := kubernetes.NewForConfig(c)
46 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
47 | //
48 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
49 | // correctly.
50 | var AddToScheme = localSchemeBuilder.AddToScheme
51 |
52 | func init() {
53 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
54 | utilruntime.Must(AddToScheme(Scheme))
55 | }
56 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/ISBRollout.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo } from "react";
2 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
3 | import { Box } from "@mui/material";
4 | import { SquareCheckIcon } from "../utils/SquareCheckIcon";
5 | import { SquareCancelIcon } from "../utils/SquareCancelIcon";
6 | import { ISB, ISB_KUBERNETES, POD } from "../utils/Constants";
7 |
8 | export const ISBRollout = () => {
9 | const { kindToNodeMap } = useContext(RolloutComponentContext);
10 |
11 | const isbPods = useMemo(() => {
12 | if (!kindToNodeMap || !kindToNodeMap.get(POD)) {
13 | return [];
14 | }
15 | const pods = kindToNodeMap.get(POD);
16 | if (!pods) {
17 | return [];
18 | }
19 | return pods.filter((node) => node.name.indexOf(ISB_KUBERNETES) >= 0);
20 | }, [kindToNodeMap]);
21 |
22 | const isbName = useMemo(() => {
23 | if (!kindToNodeMap || !kindToNodeMap.get(POD)) {
24 | return "";
25 | }
26 | const isbService = kindToNodeMap.get(ISB);
27 | if (!isbService || !isbService[0]) {
28 | return "";
29 | }
30 | return isbService[0].name;
31 | }, [kindToNodeMap]);
32 |
33 | return (
34 |
35 | ISB Name : {isbName}
36 |
37 | ISB Pod Status:{" "}
38 |
51 | {isbPods.map((node) => {
52 | return (
53 |
54 |
55 | {node?.health?.status === "Healthy" ? (
56 |
57 | ) : (
58 |
59 | )}
60 |
61 |
62 | );
63 | })}
64 |
65 |
66 |
67 | );
68 | };
69 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/structured_util.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | corev1 "k8s.io/api/core/v1"
8 | policyv1 "k8s.io/api/policy/v1"
9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 | "k8s.io/apimachinery/pkg/util/intstr"
11 | k8sClient "sigs.k8s.io/controller-runtime/pkg/client"
12 | )
13 |
14 | // this file contains utility functions for working with standard Kubernetes types
15 | // (using their typed structs as opposed to Unstructured type)
16 |
17 | // GetSecret gets secret using the kubernetes client
18 | func GetSecret(ctx context.Context, client k8sClient.Client, namespace, secretName string) (*corev1.Secret, error) {
19 | if namespace == "" {
20 | return nil, fmt.Errorf("namespace cannot be empty")
21 | }
22 | if secretName == "" {
23 | return nil, fmt.Errorf("secretName cannot be empty")
24 | }
25 | secret := &corev1.Secret{}
26 | key := k8sClient.ObjectKey{
27 | Namespace: namespace,
28 | Name: secretName,
29 | }
30 | if err := client.Get(ctx, key, secret); err != nil {
31 | return nil, err
32 | }
33 | return secret, nil
34 | }
35 |
36 | func NewPodDisruptionBudget(name, namespace string, maxUnavailable int32, ownerReference []metav1.OwnerReference) *policyv1.PodDisruptionBudget {
37 | return &policyv1.PodDisruptionBudget{
38 | ObjectMeta: metav1.ObjectMeta{
39 | Name: name,
40 | Namespace: namespace,
41 | OwnerReferences: ownerReference,
42 | },
43 | Spec: policyv1.PodDisruptionBudgetSpec{
44 | MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: maxUnavailable},
45 | Selector: &metav1.LabelSelector{
46 | MatchLabels: map[string]string{
47 | "app.kubernetes.io/component": "isbsvc",
48 | "numaflow.numaproj.io/isbsvc-name": name,
49 | },
50 | },
51 | },
52 | }
53 | }
54 |
55 | func ListPodsMetadataOnly(ctx context.Context, c k8sClient.Client, namespace, labels string) (*metav1.PartialObjectMetadataList, error) {
56 | podsMeta := &metav1.PartialObjectMetadataList{}
57 |
58 | err := KubernetesClient.CoreV1().RESTClient().
59 | Get().
60 | Namespace(namespace).
61 | Resource("pods").
62 | Param("labelSelector", labels).
63 | SetHeader("Accept", "application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1").
64 | Do(ctx).
65 | Into(podsMeta)
66 |
67 | if err != nil {
68 | return nil, err
69 | }
70 |
71 | return podsMeta, nil
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/apis/numaplane/v1alpha1/numaflowcontroller_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25 |
26 | // NumaflowControllerSpec defines the desired state of NumaflowController
27 | type NumaflowControllerSpec struct {
28 | InstanceID string `json:"instanceID,omitempty"`
29 | Version string `json:"version"`
30 | }
31 |
32 | // NumaflowControllerStatus defines the observed state of NumaflowController
33 | type NumaflowControllerStatus struct {
34 | Status `json:",inline"`
35 | }
36 |
37 | // +genclient
38 | // +kubebuilder:object:root=true
39 | // +kubebuilder:subresource:status
40 | // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
41 | // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="The current phase"
42 | // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="The desired Numaflow Controller version"
43 | // NumaflowController is the Schema for the numaflowcontrollers API
44 | type NumaflowController struct {
45 | metav1.TypeMeta `json:",inline"`
46 | metav1.ObjectMeta `json:"metadata,omitempty"`
47 |
48 | Spec NumaflowControllerSpec `json:"spec,omitempty"`
49 | Status NumaflowControllerStatus `json:"status,omitempty"`
50 | }
51 |
52 | //+kubebuilder:object:root=true
53 |
54 | // NumaflowControllerList contains a list of NumaflowController
55 | type NumaflowControllerList struct {
56 | metav1.TypeMeta `json:",inline"`
57 | metav1.ListMeta `json:"metadata,omitempty"`
58 | Items []NumaflowController `json:"items"`
59 | }
60 |
61 | func init() {
62 | SchemeBuilder.Register(&NumaflowController{}, &NumaflowControllerList{})
63 | }
64 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "numarollout",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emotion/react": "^11.11.4",
7 | "@emotion/styled": "^11.11.5",
8 | "@fortawesome/fontawesome-svg-core": "^6.5.2",
9 | "@fortawesome/free-solid-svg-icons": "^6.5.2",
10 | "@fortawesome/react-fontawesome": "^0.2.2",
11 | "@mui/icons-material": "^5.15.19",
12 | "@mui/material": "^5.15.19",
13 | "@testing-library/jest-dom": "^6.4.6",
14 | "@testing-library/react": "^16.0.0",
15 | "@testing-library/user-event": "^13.5.0",
16 | "@types/jest": "^27.5.2",
17 | "@types/node": "^16.18.98",
18 | "@types/react": "^18.3.3",
19 | "@types/react-dom": "^18.3.0",
20 | "add": "^2.0.6",
21 | "react": "^18.3.1",
22 | "react-dom": "^18.3.1",
23 | "react-scripts": "5.0.1",
24 | "typescript": "^4.9.5",
25 | "url": "^0.11.3",
26 | "yarn": "^1.22.22"
27 | },
28 | "peerDependencies": {
29 | "moment": "^2.29.4",
30 | "react": "^16.9.3",
31 | "react-dom": "^16.9.3"
32 | },
33 | "devDependencies": {
34 | "@testing-library/dom": "^10.2.0",
35 | "@types/react": "^17.0.44",
36 | "@types/react-dom": "^17.0.9",
37 | "@types/react-helmet": "^6.1.0",
38 | "@types/react-router-dom": "^5.1.8",
39 | "@types/styled-components": "^5.1.25",
40 | "babel-preset-react": "^6.24.1",
41 | "esbuild-loader": "^3.0.1",
42 | "portable-fetch": "^3.0.0",
43 | "raw-loader": "0.5.1",
44 | "react-dom": "^17.0.2",
45 | "react-keyhooks": "^0.2.3",
46 | "rxjs": "^7.1.0",
47 | "sass": "1.34.1",
48 | "sass-loader": "10.2.1",
49 | "style-loader": "1.3.0",
50 | "ts-loader": "8.2.0",
51 | "typescript": "^4.3.5",
52 | "webpack": "^5.75.0",
53 | "webpack-bundle-analyzer": "^4.8.0",
54 | "webpack-cli": "^4.7.2"
55 | },
56 | "scripts": {
57 | "start": "webpack --config ./webpack.config.js --watch",
58 | "build": "webpack --config ./webpack.config.js && tar -C dist -cvf extension.tar resources",
59 | "test": "react-scripts test"
60 | },
61 | "eslintConfig": {
62 | "extends": [
63 | "react-app",
64 | "react-app/jest"
65 | ]
66 | },
67 | "browserslist": {
68 | "production": [
69 | ">0.2%",
70 | "not dead",
71 | "not op_mini all"
72 | ],
73 | "development": [
74 | "last 1 chrome version",
75 | "last 1 firefox version",
76 | "last 1 safari version"
77 | ]
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/internal/controller/config/usde_config.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package config
18 |
19 | import (
20 | "fmt"
21 |
22 | "github.com/rs/zerolog/log"
23 | )
24 |
25 | type SpecField struct {
26 | Path string `json:"path" yaml:"path"`
27 | IncludeSubfields bool `json:"includeSubfields,omitempty" yaml:"includeSubfields,omitempty"`
28 | }
29 |
30 | type USDEResourceConfig struct {
31 | // Recreate indicates fields that require the resource to be recreated upon modification.
32 | // For PPND strategy, this list is checked before the other two lists.
33 | Recreate []SpecField `json:"recreate,omitempty" yaml:"recreate,omitempty"`
34 | // DataLoss represents fields that, when changed, may result in data loss.
35 | // For PPND strategy, this list is checked after the 'recreate' list.
36 | DataLoss []SpecField `json:"dataLoss,omitempty" yaml:"dataLoss,omitempty"`
37 | // Progressive contains fields that can be updated without requiring a full resource recreation and by performing an in-place update.
38 | // For PPND strategy, this list is checked after the other two lists.
39 | Progressive []SpecField `json:"progressive,omitempty" yaml:"progressive,omitempty"`
40 | }
41 |
42 | type USDEConfig map[string]USDEResourceConfig
43 |
44 | func (cm *ConfigManager) UpdateUSDEConfig(config USDEConfig) {
45 | cm.usdeConfigLock.Lock()
46 | defer cm.usdeConfigLock.Unlock()
47 |
48 | cm.usdeConfig = config
49 |
50 | log.Debug().Msg(fmt.Sprintf("USDE Config update: %+v", config)) // due to cyclical dependency, we can't call logger
51 | }
52 |
53 | func (cm *ConfigManager) UnsetUSDEConfig() {
54 | cm.usdeConfigLock.Lock()
55 | defer cm.usdeConfigLock.Unlock()
56 |
57 | cm.usdeConfig = USDEConfig{}
58 |
59 | log.Debug().Msg("USDE Config unset") // due to cyclical dependency, we can't call logger
60 | }
61 |
62 | func (cm *ConfigManager) GetUSDEConfig() USDEConfig {
63 | cm.usdeConfigLock.Lock()
64 | defer cm.usdeConfigLock.Unlock()
65 |
66 | return cm.usdeConfig
67 | }
68 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutSummary.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import "./ArgoSummary.css";
4 |
5 | interface ArgoRolloutSummaryProps {
6 | rolloutParams: {
7 | strategy: string;
8 | setWeight: string;
9 | actualWeight: string;
10 | };
11 | }
12 | export const ArgoRolloutSummary = ({
13 | rolloutParams,
14 | }: ArgoRolloutSummaryProps) => {
15 | return (
16 |
17 |
Summary
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
{rolloutParams.strategy}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
1/1
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
{rolloutParams.setWeight}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
{rolloutParams.actualWeight}
68 |
69 |
70 |
71 |
72 |
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "js-tokens@^3.0.0 || ^4.0.0":
6 | version "4.0.0"
7 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
8 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
9 |
10 | loose-envify@^1.1.0:
11 | version "1.4.0"
12 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
13 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
14 | dependencies:
15 | js-tokens "^3.0.0 || ^4.0.0"
16 |
17 | object-assign@^4.1.1:
18 | version "4.1.1"
19 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
20 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
21 |
22 | "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.3.1:
23 | version "18.3.1"
24 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
25 | integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
26 |
27 | react-shallow-renderer@^16.15.0:
28 | version "16.15.0"
29 | resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
30 | integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
31 | dependencies:
32 | object-assign "^4.1.1"
33 | react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
34 |
35 | react-test-renderer@^18.2.0:
36 | version "18.3.1"
37 | resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4"
38 | integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==
39 | dependencies:
40 | react-is "^18.3.1"
41 | react-shallow-renderer "^16.15.0"
42 | scheduler "^0.23.2"
43 |
44 | scheduler@^0.23.2:
45 | version "0.23.2"
46 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
47 | integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
48 | dependencies:
49 | loose-envify "^1.1.0"
50 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/ControllerRollout.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo } from "react";
2 | import { Box } from "@mui/material";
3 | import { RolloutComponentContext } from "./RolloutComponentWrapper";
4 | import { SquareCheckIcon } from "../utils/SquareCheckIcon";
5 | import { SquareCancelIcon } from "../utils/SquareCancelIcon";
6 |
7 | export const ControllerRollout = () => {
8 | const { kindToNodeMap } = useContext(RolloutComponentContext);
9 |
10 | const controllerPods = useMemo(() => {
11 | const pods = kindToNodeMap?.get("Pod") ?? [];
12 | return pods.filter(
13 | (node) => node?.name?.indexOf("numaflow-controller") >= 0
14 | );
15 | }, [kindToNodeMap?.get("Pod")]);
16 |
17 | const controllerName = useMemo(() => {
18 | const numaflowController = kindToNodeMap?.get(
19 | "NumaflowControllerRollout"
20 | )?.[0];
21 | if (numaflowController) {
22 | const numaControllerName = numaflowController.name;
23 |
24 | // Find the deployment object with the numaControllerName
25 | const deployment = kindToNodeMap.get("Deployment") ?? [];
26 | const controllerDeployment = deployment.find(
27 | (node) => node?.parentRefs?.[0]?.name === numaControllerName
28 | );
29 |
30 | return controllerDeployment?.name ?? "";
31 | }
32 | return "";
33 | }, [
34 | kindToNodeMap.get("NumaflowControllerRollout"),
35 | kindToNodeMap.get("Deployment"),
36 | ]);
37 |
38 | return (
39 |
40 | Controller Name : {controllerName}
41 |
42 | Controller Pod Status:{" "}
43 |
56 | {controllerPods.map((node) => {
57 | return (
58 |
59 |
60 | {node?.health?.status === "Healthy" ? (
61 |
62 | ) : (
63 |
64 | )}
65 |
66 |
67 | );
68 | })}
69 |
70 |
71 |
72 | );
73 | };
74 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/expansion_generated.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | // ISBServiceRolloutListerExpansion allows custom methods to be added to
21 | // ISBServiceRolloutLister.
22 | type ISBServiceRolloutListerExpansion interface{}
23 |
24 | // ISBServiceRolloutNamespaceListerExpansion allows custom methods to be added to
25 | // ISBServiceRolloutNamespaceLister.
26 | type ISBServiceRolloutNamespaceListerExpansion interface{}
27 |
28 | // MonoVertexRolloutListerExpansion allows custom methods to be added to
29 | // MonoVertexRolloutLister.
30 | type MonoVertexRolloutListerExpansion interface{}
31 |
32 | // MonoVertexRolloutNamespaceListerExpansion allows custom methods to be added to
33 | // MonoVertexRolloutNamespaceLister.
34 | type MonoVertexRolloutNamespaceListerExpansion interface{}
35 |
36 | // NumaflowControllerListerExpansion allows custom methods to be added to
37 | // NumaflowControllerLister.
38 | type NumaflowControllerListerExpansion interface{}
39 |
40 | // NumaflowControllerNamespaceListerExpansion allows custom methods to be added to
41 | // NumaflowControllerNamespaceLister.
42 | type NumaflowControllerNamespaceListerExpansion interface{}
43 |
44 | // NumaflowControllerRolloutListerExpansion allows custom methods to be added to
45 | // NumaflowControllerRolloutLister.
46 | type NumaflowControllerRolloutListerExpansion interface{}
47 |
48 | // NumaflowControllerRolloutNamespaceListerExpansion allows custom methods to be added to
49 | // NumaflowControllerRolloutNamespaceLister.
50 | type NumaflowControllerRolloutNamespaceListerExpansion interface{}
51 |
52 | // PipelineRolloutListerExpansion allows custom methods to be added to
53 | // PipelineRolloutLister.
54 | type PipelineRolloutListerExpansion interface{}
55 |
56 | // PipelineRolloutNamespaceListerExpansion allows custom methods to be added to
57 | // PipelineRolloutNamespaceLister.
58 | type PipelineRolloutNamespaceListerExpansion interface{}
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # numaplane
2 | Numaplane is a control plane for installing, managing and running numaflow resources on Kubernetes.
3 |
4 | ## Getting Started
5 |
6 | ### Prerequisites
7 | - go version v1.20.0+
8 | - docker version 17.03+.
9 | - kubectl version v1.11.3+.
10 | - Access to a Kubernetes v1.11.3+ cluster.
11 |
12 | ### To build Numaplane image and run it on your local cluster with latest manifests
13 |
14 | `make start`
15 |
16 |
17 | ### To auto-generate code and manifests from Go
18 |
19 | `make codegen`
20 |
21 |
22 | ## Contributing
23 | **NOTE:** Run `make --help` for more information on all potential `make` targets
24 |
25 | ## How To Release
26 |
27 | ### Release Branch
28 |
29 | Always create a release branch for the releases, for example branch `release-0.5` is for all the v0.5.x versions release.
30 | If it's a new release branch, simply create a branch from `main`.
31 |
32 | ### Release Steps
33 |
34 | 1. Cherry-pick fixes to the release branch, skip this and the following two steps if it's the first release in the branch.
35 | 2. Run `make test` to make sure all test cases pass locally.
36 | 3. Push to remote branch, and make sure all the CI jobs pass.
37 | 4. Run `make prepare-release VERSION=v{x.y.z}` to update version in manifests, where `x.y.z` is the expected new version.
38 | 5. Follow the output of last step, to confirm if all the changes are expected, and then run `make release VERSION=v{x.y.z}`.
39 | 6. Follow the output, push a new tag to the release branch, GitHub actions will automatically build and publish the new release,
40 | this will take around 10 minutes.
41 | 7. Test the new release, make sure everything is running as expected, and then recreate a `stable` tag against the latest release.
42 | ```shell
43 | git tag -d stable
44 | git tag -a stable -m stable
45 | git push -d {your-remote} stable
46 | git push {your-remote} stable
47 | ```
48 | 8. Find the new release tag, and edit the release notes.
49 |
50 |
51 | ## License
52 |
53 | Copyright 2023 The Numaproj Authors.
54 |
55 | Licensed under the Apache License, Version 2.0 (the "License");
56 | you may not use this file except in compliance with the License.
57 | You may obtain a copy of the License at
58 |
59 | http://www.apache.org/licenses/LICENSE-2.0
60 |
61 | Unless required by applicable law or agreed to in writing, software
62 | distributed under the License is distributed on an "AS IS" BASIS,
63 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
64 | See the License for the specific language governing permissions and
65 | limitations under the License.
66 |
--------------------------------------------------------------------------------
/pkg/apis/numaplane/v1alpha1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1alpha1 contains API Schema definitions for the numaplane.numaproj.io v1alpha1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=numaplane.numaproj.io
20 | package v1alpha1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // SchemeGroupVersion is group version used to register these objects
29 | SchemeGroupVersion = schema.GroupVersion{Group: "numaplane.numaproj.io", Version: "v1alpha1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 |
37 | ISBServiceRolloutGroupVersionKind = SchemeGroupVersion.WithKind("ISBServiceRollout")
38 | ISBServiceRolloutGroupVersionResource = SchemeGroupVersion.WithResource("isbservicerollouts")
39 |
40 | PipelineRolloutGroupVersionKind = SchemeGroupVersion.WithKind("PipelineRollout")
41 | PipelineRolloutGroupVersionResource = SchemeGroupVersion.WithResource("pipelinerollouts")
42 |
43 | NumaflowControllerRolloutGroupVersionKind = SchemeGroupVersion.WithKind("NumaflowControllerRollout")
44 | NumaflowControllerRolloutGroupVersionResource = SchemeGroupVersion.WithResource("numaflowcontrollerrollouts")
45 |
46 | MonoVertexRolloutGroupVersionKind = SchemeGroupVersion.WithKind("MonoVertexRollout")
47 | MonoVertexRolloutGroupVersionResource = SchemeGroupVersion.WithResource("monovertexrollouts")
48 |
49 | NumaflowControllerGroupVersionKind = SchemeGroupVersion.WithKind("NumaflowController")
50 | NumaflowControllerGroupVersionResource = SchemeGroupVersion.WithResource("numaflowcontrollers")
51 | )
52 |
53 | // Resource takes an unqualified resource and returns a Group qualified GroupResource
54 | func Resource(resource string) schema.GroupResource {
55 | return SchemeGroupVersion.WithResource(resource).GroupResource()
56 | }
57 |
--------------------------------------------------------------------------------
/internal/controller/common/predicate_filters.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package common
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | "sigs.k8s.io/controller-runtime/pkg/event"
22 | "sigs.k8s.io/controller-runtime/pkg/predicate"
23 | )
24 |
25 | /*
26 | Reference: https://github.com/kubernetes-sigs/controller-runtime/issues/2355#issuecomment-2477548322
27 | There's not an explicit attribute that holds this info about sync trigger. However, the "update" events that come in
28 | when these batches of resync-triggered events happen, the resource version changes for every real update,
29 | but doesn't for the resync-triggered events. So, to detect these cases while also making use of the generation change check,
30 | added a custom predicate that is a logical OR of checking
31 | 1. If the generation changed between the old and new object.
32 | 2. If the resource version remained the same between the old and new object.
33 | Assuming there are no other edge cases where the resource version remains the same across an update event
34 | */
35 |
36 | type TypedGenerationChangedPredicate[object metav1.Object] struct {
37 | // Returns true by default for all events; see overrides below.
38 | predicate.TypedFuncs[object]
39 | }
40 |
41 | func (p TypedGenerationChangedPredicate[object]) Update(e event.TypedUpdateEvent[object]) bool {
42 | // It will effectively check of ObjectOld and ObjectNew does not exist or if the name of the object is empty
43 | if e.ObjectOld.GetName() == "" || e.ObjectNew.GetName() == "" {
44 | return false
45 | }
46 |
47 | // Process the event if the generation changed (which happens e.g. if the spec
48 | // was updated, but not if the status or metadata fields were updated).
49 | if e.ObjectNew.GetGeneration() != e.ObjectOld.GetGeneration() {
50 | return true
51 | }
52 |
53 | // If the generation is unchanged, we generally don't want to process the event,
54 | // except events triggered by periodic resyncs (which are, for some
55 | // reason, categorized as updates). We identify such events by checking if the old
56 | // and new resource versions are equal.
57 | if e.ObjectNew.GetResourceVersion() == e.ObjectOld.GetResourceVersion() {
58 | return true
59 | }
60 |
61 | return false
62 | }
63 |
--------------------------------------------------------------------------------
/internal/util/kubernetes/kubernetes_util.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "fmt"
5 | "regexp"
6 | "strings"
7 |
8 | "k8s.io/apimachinery/pkg/util/validation"
9 | )
10 |
11 | // this file is designed to hold utility functions for Kubernetes that are not specific to
12 | // any type of resource
13 |
14 | // validManifestExtensions contains the supported extension for raw file.
15 | var validManifestExtensions = map[string]struct{}{"yaml": {}, "yml": {}, "json": {}}
16 |
17 | func IsValidKubernetesNamespace(name string) bool {
18 | // All namespace names must be valid RFC 1123 DNS labels.
19 | errs := validation.IsDNS1123Label(name)
20 | reservedNamesRegex := regexp.MustCompile(`^(kubernetes-|kube-)`)
21 | if len(errs) == 0 && !reservedNamesRegex.MatchString(name) {
22 | return true
23 | }
24 | return false
25 | }
26 |
27 | func IsValidKubernetesManifestFile(fileName string) bool {
28 | fileExt := strings.Split(fileName, ".")
29 | if _, ok := validManifestExtensions[fileExt[len(fileExt)-1]]; ok {
30 | return true
31 | }
32 | return false
33 | }
34 |
35 | // ResourceFilter filter resources based on allowed Resource Types
36 | type ResourceFilter struct {
37 | IncludedResources []ResourceType
38 | }
39 |
40 | type ResourceType struct {
41 | Group string
42 | Kind string
43 | }
44 |
45 | func (n *ResourceFilter) IsExcludedResource(group, kind, _ string) bool {
46 | for _, resource := range n.IncludedResources {
47 | if resource.Kind == "" {
48 | // When Kind is empty, we only check if Group matches
49 | if group == resource.Group {
50 | return false
51 | }
52 | } else if group == resource.Group && kind == resource.Kind {
53 | return false
54 | }
55 | }
56 | return true
57 | }
58 |
59 | // ParseResourceFilter parse the given rules to generate the
60 | // list of resources we allow or watch. The rules are delimited by ';', and
61 | // each rule is composed by both 'group' and 'kind' which can be empty.
62 | // For example, 'group=apps,kind=Deployment;group=,kind=ConfigMap'.
63 | // Note that empty rule is valid.
64 | func ParseResourceFilter(rules string) ([]ResourceType, error) {
65 | filteredResources := make([]ResourceType, 0)
66 | if rules == "" {
67 | return filteredResources, nil
68 | }
69 | rulesArr := strings.Split(rules, ";")
70 | err := fmt.Errorf("malformed resource filter rules %q", rules)
71 | for _, rule := range rulesArr {
72 | ruleArr := strings.Split(rule, ",")
73 | if len(ruleArr) != 2 {
74 | return nil, err
75 | }
76 | groupArr := strings.Split(ruleArr[0], "=")
77 | kindArr := strings.Split(ruleArr[1], "=")
78 | if !strings.EqualFold(groupArr[0], "group") || !strings.EqualFold(kindArr[0], "kind") {
79 | return nil, err
80 | }
81 | filteredResource := ResourceType{
82 | Group: groupArr[1],
83 | Kind: kindArr[1],
84 | }
85 | filteredResources = append(filteredResources, filteredResource)
86 | }
87 | return filteredResources, nil
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/config/samples/numaplane.numaproj.io_v1alpha1_pipelinerollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: numaplane.numaproj.io/v1alpha1
2 | kind: PipelineRollout
3 | metadata:
4 | name: my-pipeline
5 | namespace: example-namespace
6 | spec:
7 | strategy:
8 | progressive:
9 | assessmentSchedule: "10,200,60,10"
10 | # analysis:
11 | # args:
12 | #templates:
13 | #- templateName: pipeline-template
14 | # clusterScope: false
15 | riders:
16 | - perVertex: true
17 | definition:
18 | apiVersion: autoscaling.k8s.io/v1beta2
19 | kind: VerticalPodAutoscaler
20 | metadata:
21 | name: vpa
22 | spec:
23 | targetRef:
24 | apiVersion: numaproj.io/v1alpha1
25 | kind: Vertex
26 | name: '{{.pipeline-name}}-{{.vertex-name}}'
27 | # - perVertex: true
28 | # definition:
29 | # apiVersion: autoscaling/v2
30 | # kind: HorizontalPodAutoscaler
31 | # metadata:
32 | # name: hpa
33 | # spec:
34 | # minReplicas: 1
35 | # maxReplicas: 10
36 | # metrics:
37 | # - object:
38 | # metric:
39 | # name: namespace_app_monovertex_container_cpu_utilization
40 | # selector:
41 | # matchLabels:
42 | # container: udsource
43 | # target:
44 | # type: Value
45 | # value: 80
46 | # describedObject:
47 | # apiVersion: apps/v1
48 | # kind: Deployment
49 | # name: '{{.pipeline-name}}-{{.vertex-name}}'
50 | # type: Object
51 | # scaleTargetRef:
52 | # apiVersion: numaflow.numaproj.io/v1alpha1
53 | # kind: Vertex
54 | # name: '{{.pipeline-name}}-{{.vertex-name}}'
55 |
56 | pipeline:
57 | metadata:
58 | annotations:
59 | # numaflow.numaproj.io/instance: "0" # uncomment for Progressive rollout to set Numaflow Controller instance
60 | my-templated-annotation: '{{.pipeline-namespace}}-{{.pipeline-name}}'
61 |
62 | spec:
63 | lifecycle:
64 | pauseGracePeriodSeconds: 60
65 | interStepBufferServiceName: my-isbsvc
66 | vertices:
67 | - name: in
68 | scale:
69 | #min: 3
70 | #max: 5
71 | loopbackSeconds: 180
72 |
73 | source:
74 | # A self data generating source
75 | generator:
76 | rpu: 500
77 | duration: 1s
78 | - name: cat
79 | scale:
80 | min: 3
81 | max: 5
82 | udf:
83 | container:
84 | image: "quay.io/numaio/numaflow-go/map-cat:stable" # A built-in UDF which simply cats the message
85 | imagePullPolicy: Always
86 | - name: out
87 | sink:
88 | # A simple log printing sink
89 | log: {}
90 | edges:
91 | - from: in
92 | to: cat
93 | - from: cat
94 | to: out
95 |
--------------------------------------------------------------------------------
/tests/e2e/analysistemplate.go:
--------------------------------------------------------------------------------
1 | package e2e
2 |
3 | import (
4 | "fmt"
5 |
6 | . "github.com/onsi/ginkgo/v2"
7 | . "github.com/onsi/gomega"
8 | "k8s.io/apimachinery/pkg/api/errors"
9 |
10 | argov1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 | )
13 |
14 | func CreateAnalysisTemplate(name, namespace string, spec argov1alpha1.AnalysisTemplateSpec) {
15 | analysisTemplateSpec := createAnalysisTemplateSpec(name, namespace, spec)
16 | _, err := argoAnalysisTemplateClient.Create(ctx, analysisTemplateSpec, metav1.CreateOptions{})
17 | Expect(err).ShouldNot(HaveOccurred())
18 |
19 | CheckEventually("Verifying that the Analysis Template was created", func() error {
20 | _, err := argoAnalysisTemplateClient.Get(ctx, name, metav1.GetOptions{})
21 | return err
22 | }).Should(Succeed())
23 | }
24 |
25 | func createAnalysisTemplateSpec(name, namespace string, spec argov1alpha1.AnalysisTemplateSpec) *argov1alpha1.AnalysisTemplate {
26 | analysisTemplate := &argov1alpha1.AnalysisTemplate{
27 | TypeMeta: metav1.TypeMeta{
28 | APIVersion: "argoproj.io/v1alpha1",
29 | Kind: "analysisTemplate",
30 | },
31 | ObjectMeta: metav1.ObjectMeta{
32 | Name: name,
33 | Namespace: namespace,
34 | },
35 | Spec: argov1alpha1.AnalysisTemplateSpec{
36 | Metrics: spec.Metrics,
37 | Args: spec.Args,
38 | },
39 | }
40 |
41 | return analysisTemplate
42 | }
43 |
44 | // DeleteAnalysisTemplate will delete and verify deletion of the Analysis Template with the given name.
45 | func DeleteAnalysisTemplate(name string) {
46 | By("Deleting AnalysisTemplate")
47 | foregroundDeletion := metav1.DeletePropagationForeground
48 | err := argoAnalysisTemplateClient.Delete(ctx, name, metav1.DeleteOptions{PropagationPolicy: &foregroundDeletion})
49 | Expect(err).ShouldNot(HaveOccurred())
50 |
51 | CheckEventually(fmt.Sprintf("Verifying AnalysisTemplate deletion, (%s)", name), func() bool {
52 | _, err := argoAnalysisTemplateClient.Get(ctx, name, metav1.GetOptions{})
53 | if err != nil {
54 | if !errors.IsNotFound(err) {
55 | Fail("An unexpected error occurred when fetching the AnalysisTemplate: " + err.Error())
56 | }
57 | return false
58 | }
59 | return true
60 | }).WithTimeout(DefaultTestTimeout).Should(BeFalse(), "The AnalysisTemplate should have been deleted but it was found.")
61 | }
62 |
63 | func VerifyAnalysisRunStatus(metricName, name string, expectedStatus argov1alpha1.AnalysisPhase) {
64 | CheckEventually(fmt.Sprintf("Verifying AnalysisRun status (%s)", name), func() bool {
65 | analysisRun, err := argoAnalysisRunClient.Get(ctx, name, metav1.GetOptions{})
66 | if err != nil {
67 | if !errors.IsNotFound(err) {
68 | Fail("An unexpected error occurred when fetching the AnalysisTemplate: " + err.Error())
69 | }
70 | return false
71 | }
72 | for _, metric := range analysisRun.Status.MetricResults {
73 | if metric.Name == metricName {
74 | if metric.Phase == expectedStatus {
75 | return true
76 | }
77 | }
78 | }
79 | return false
80 | }).Should(BeTrue())
81 | }
82 |
--------------------------------------------------------------------------------
/pkg/client/informers/externalversions/generic.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by informer-gen. DO NOT EDIT.
17 |
18 | package externalversions
19 |
20 | import (
21 | "fmt"
22 |
23 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
24 | schema "k8s.io/apimachinery/pkg/runtime/schema"
25 | cache "k8s.io/client-go/tools/cache"
26 | )
27 |
28 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other
29 | // sharedInformers based on type
30 | type GenericInformer interface {
31 | Informer() cache.SharedIndexInformer
32 | Lister() cache.GenericLister
33 | }
34 |
35 | type genericInformer struct {
36 | informer cache.SharedIndexInformer
37 | resource schema.GroupResource
38 | }
39 |
40 | // Informer returns the SharedIndexInformer.
41 | func (f *genericInformer) Informer() cache.SharedIndexInformer {
42 | return f.informer
43 | }
44 |
45 | // Lister returns the GenericLister.
46 | func (f *genericInformer) Lister() cache.GenericLister {
47 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
48 | }
49 |
50 | // ForResource gives generic access to a shared informer of the matching type
51 | // TODO extend this to unknown resources with a client pool
52 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
53 | switch resource {
54 | // Group=numaplane, Version=v1alpha1
55 | case v1alpha1.SchemeGroupVersion.WithResource("isbservicerollouts"):
56 | return &genericInformer{resource: resource.GroupResource(), informer: f.Numaplane().V1alpha1().ISBServiceRollouts().Informer()}, nil
57 | case v1alpha1.SchemeGroupVersion.WithResource("monovertexrollouts"):
58 | return &genericInformer{resource: resource.GroupResource(), informer: f.Numaplane().V1alpha1().MonoVertexRollouts().Informer()}, nil
59 | case v1alpha1.SchemeGroupVersion.WithResource("numaflowcontrollers"):
60 | return &genericInformer{resource: resource.GroupResource(), informer: f.Numaplane().V1alpha1().NumaflowControllers().Informer()}, nil
61 | case v1alpha1.SchemeGroupVersion.WithResource("numaflowcontrollerrollouts"):
62 | return &genericInformer{resource: resource.GroupResource(), informer: f.Numaplane().V1alpha1().NumaflowControllerRollouts().Informer()}, nil
63 | case v1alpha1.SchemeGroupVersion.WithResource("pipelinerollouts"):
64 | return &genericInformer{resource: resource.GroupResource(), informer: f.Numaplane().V1alpha1().PipelineRollouts().Informer()}, nil
65 |
66 | }
67 |
68 | return nil, fmt.Errorf("no informer found for %v", resource)
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/apis/numaplane/v1alpha1/numaflowcontrollerrollout_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25 |
26 | type Controller struct {
27 | // NOTE: keeping the instanceID also in the NumaflowControllerRollout in case users want to
28 | // create multiple Numaflow controllers within the same namespace
29 | InstanceID string `json:"instanceID,omitempty"`
30 | Version string `json:"version"`
31 | }
32 |
33 | // NumaflowControllerRolloutSpec defines the desired state of NumaflowControllerRollout
34 | type NumaflowControllerRolloutSpec struct {
35 | Controller Controller `json:"controller"`
36 | }
37 |
38 | // NumaflowControllerRolloutStatus defines the observed state of NumaflowControllerRollout
39 | type NumaflowControllerRolloutStatus struct {
40 | Status `json:",inline"`
41 | PauseRequestStatus PauseStatus `json:"pauseRequestStatus,omitempty"`
42 | }
43 |
44 | // +genclient
45 | // +kubebuilder:object:root=true
46 | // +kubebuilder:subresource:status
47 | // +kubebuilder:validation:XValidation:rule="matches(self.metadata.name, '^numaflow-controller.*')",message="The metadata name must start with 'numaflow-controller'"
48 | // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
49 | // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="The current phase"
50 | // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.controller.version",description="The desired Numaflow Controller version"
51 | // NumaflowControllerRollout is the Schema for the numaflowcontrollerrollouts API
52 | type NumaflowControllerRollout struct {
53 | metav1.TypeMeta `json:",inline"`
54 | metav1.ObjectMeta `json:"metadata,omitempty"`
55 |
56 | Spec NumaflowControllerRolloutSpec `json:"spec,omitempty"`
57 | Status NumaflowControllerRolloutStatus `json:"status,omitempty"`
58 | }
59 |
60 | //+kubebuilder:object:root=true
61 |
62 | // NumaflowControllerRolloutList contains a list of NumaflowControllerRollout
63 | type NumaflowControllerRolloutList struct {
64 | metav1.TypeMeta `json:",inline"`
65 | metav1.ListMeta `json:"metadata,omitempty"`
66 | Items []NumaflowControllerRollout `json:"items"`
67 | }
68 |
69 | func init() {
70 | SchemeBuilder.Register(&NumaflowControllerRollout{}, &NumaflowControllerRolloutList{})
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/pipelinerollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | "k8s.io/apimachinery/pkg/labels"
23 | "k8s.io/client-go/listers"
24 | "k8s.io/client-go/tools/cache"
25 | )
26 |
27 | // PipelineRolloutLister helps list PipelineRollouts.
28 | // All objects returned here must be treated as read-only.
29 | type PipelineRolloutLister interface {
30 | // List lists all PipelineRollouts in the indexer.
31 | // Objects returned here must be treated as read-only.
32 | List(selector labels.Selector) (ret []*v1alpha1.PipelineRollout, err error)
33 | // PipelineRollouts returns an object that can list and get PipelineRollouts.
34 | PipelineRollouts(namespace string) PipelineRolloutNamespaceLister
35 | PipelineRolloutListerExpansion
36 | }
37 |
38 | // pipelineRolloutLister implements the PipelineRolloutLister interface.
39 | type pipelineRolloutLister struct {
40 | listers.ResourceIndexer[*v1alpha1.PipelineRollout]
41 | }
42 |
43 | // NewPipelineRolloutLister returns a new PipelineRolloutLister.
44 | func NewPipelineRolloutLister(indexer cache.Indexer) PipelineRolloutLister {
45 | return &pipelineRolloutLister{listers.New[*v1alpha1.PipelineRollout](indexer, v1alpha1.Resource("pipelinerollout"))}
46 | }
47 |
48 | // PipelineRollouts returns an object that can list and get PipelineRollouts.
49 | func (s *pipelineRolloutLister) PipelineRollouts(namespace string) PipelineRolloutNamespaceLister {
50 | return pipelineRolloutNamespaceLister{listers.NewNamespaced[*v1alpha1.PipelineRollout](s.ResourceIndexer, namespace)}
51 | }
52 |
53 | // PipelineRolloutNamespaceLister helps list and get PipelineRollouts.
54 | // All objects returned here must be treated as read-only.
55 | type PipelineRolloutNamespaceLister interface {
56 | // List lists all PipelineRollouts in the indexer for a given namespace.
57 | // Objects returned here must be treated as read-only.
58 | List(selector labels.Selector) (ret []*v1alpha1.PipelineRollout, err error)
59 | // Get retrieves the PipelineRollout from the indexer for a given namespace and name.
60 | // Objects returned here must be treated as read-only.
61 | Get(name string) (*v1alpha1.PipelineRollout, error)
62 | PipelineRolloutNamespaceListerExpansion
63 | }
64 |
65 | // pipelineRolloutNamespaceLister implements the PipelineRolloutNamespaceLister
66 | // interface.
67 | type pipelineRolloutNamespaceLister struct {
68 | listers.ResourceIndexer[*v1alpha1.PipelineRollout]
69 | }
70 |
--------------------------------------------------------------------------------
/internal/controller/common/numaflowtypes/monovertex.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package numaflowtypes
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | numaflowv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
24 | "github.com/numaproj/numaplane/internal/util"
25 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26 |
27 | "github.com/numaproj/numaplane/internal/util/kubernetes"
28 | "github.com/numaproj/numaplane/internal/util/logger"
29 | )
30 |
31 | type MonoVertexStatus = kubernetes.GenericStatus
32 |
33 | func ParseMonoVertexStatus(monoVertex *unstructured.Unstructured) (MonoVertexStatus, error) {
34 | if monoVertex == nil || len(monoVertex.Object) == 0 {
35 | return MonoVertexStatus{}, nil
36 | }
37 |
38 | var status MonoVertexStatus
39 | err := util.StructToStruct(monoVertex.Object["status"], &status)
40 | if err != nil {
41 | return MonoVertexStatus{}, err
42 | }
43 |
44 | return status, nil
45 | }
46 |
47 | func CheckMonoVertexPhase(ctx context.Context, monovertex *unstructured.Unstructured, phase numaflowv1.PipelinePhase) bool {
48 | numaLogger := logger.FromContext(ctx)
49 | pipelineStatus, err := ParseMonoVertexStatus(monovertex)
50 | if err != nil {
51 | numaLogger.Errorf(err, "failed to parse MonoVertex Status from monovertex CR: %+v, %v", monovertex, err)
52 | return false
53 | }
54 |
55 | return numaflowv1.PipelinePhase(pipelineStatus.Phase) == phase
56 | }
57 |
58 | func GetMonoVertexDesiredPhase(monovertex *unstructured.Unstructured) (string, error) {
59 | desiredPhase, _, err := unstructured.NestedString(monovertex.Object, "spec", "lifecycle", "desiredPhase")
60 | if err != nil {
61 | return desiredPhase, err
62 | }
63 |
64 | if desiredPhase == "" {
65 | desiredPhase = string(numaflowv1.MonoVertexPhaseRunning)
66 | }
67 | return desiredPhase, err
68 | }
69 |
70 | // CanMonoVertexIngestData verifies that the configuration of the MonoVertex would allow it to ingest data
71 | // (must be set to Running and must have scale > 0)
72 | func CanMonoVertexIngestData(ctx context.Context, monovertex *unstructured.Unstructured) (bool, error) {
73 |
74 | scaleMinMax, err := ExtractScaleMinMax(monovertex.Object, []string{"spec", "scale"})
75 | if err != nil {
76 | return false, fmt.Errorf("cannot extract the scale min and max values from the monovertex: %w", err)
77 | }
78 | zeroScale := scaleMinMax != nil && scaleMinMax.Max != nil && *scaleMinMax.Max == 0
79 | desiredPhase, err := GetMonoVertexDesiredPhase(monovertex)
80 | if err != nil {
81 | return false, err
82 | }
83 | return desiredPhase == string(numaflowv1.MonoVertexPhaseRunning) && !zeroScale, nil
84 | }
85 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/isbservicerollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | "k8s.io/apimachinery/pkg/labels"
23 | "k8s.io/client-go/listers"
24 | "k8s.io/client-go/tools/cache"
25 | )
26 |
27 | // ISBServiceRolloutLister helps list ISBServiceRollouts.
28 | // All objects returned here must be treated as read-only.
29 | type ISBServiceRolloutLister interface {
30 | // List lists all ISBServiceRollouts in the indexer.
31 | // Objects returned here must be treated as read-only.
32 | List(selector labels.Selector) (ret []*v1alpha1.ISBServiceRollout, err error)
33 | // ISBServiceRollouts returns an object that can list and get ISBServiceRollouts.
34 | ISBServiceRollouts(namespace string) ISBServiceRolloutNamespaceLister
35 | ISBServiceRolloutListerExpansion
36 | }
37 |
38 | // iSBServiceRolloutLister implements the ISBServiceRolloutLister interface.
39 | type iSBServiceRolloutLister struct {
40 | listers.ResourceIndexer[*v1alpha1.ISBServiceRollout]
41 | }
42 |
43 | // NewISBServiceRolloutLister returns a new ISBServiceRolloutLister.
44 | func NewISBServiceRolloutLister(indexer cache.Indexer) ISBServiceRolloutLister {
45 | return &iSBServiceRolloutLister{listers.New[*v1alpha1.ISBServiceRollout](indexer, v1alpha1.Resource("isbservicerollout"))}
46 | }
47 |
48 | // ISBServiceRollouts returns an object that can list and get ISBServiceRollouts.
49 | func (s *iSBServiceRolloutLister) ISBServiceRollouts(namespace string) ISBServiceRolloutNamespaceLister {
50 | return iSBServiceRolloutNamespaceLister{listers.NewNamespaced[*v1alpha1.ISBServiceRollout](s.ResourceIndexer, namespace)}
51 | }
52 |
53 | // ISBServiceRolloutNamespaceLister helps list and get ISBServiceRollouts.
54 | // All objects returned here must be treated as read-only.
55 | type ISBServiceRolloutNamespaceLister interface {
56 | // List lists all ISBServiceRollouts in the indexer for a given namespace.
57 | // Objects returned here must be treated as read-only.
58 | List(selector labels.Selector) (ret []*v1alpha1.ISBServiceRollout, err error)
59 | // Get retrieves the ISBServiceRollout from the indexer for a given namespace and name.
60 | // Objects returned here must be treated as read-only.
61 | Get(name string) (*v1alpha1.ISBServiceRollout, error)
62 | ISBServiceRolloutNamespaceListerExpansion
63 | }
64 |
65 | // iSBServiceRolloutNamespaceLister implements the ISBServiceRolloutNamespaceLister
66 | // interface.
67 | type iSBServiceRolloutNamespaceLister struct {
68 | listers.ResourceIndexer[*v1alpha1.ISBServiceRollout]
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/monovertexrollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | "k8s.io/apimachinery/pkg/labels"
23 | "k8s.io/client-go/listers"
24 | "k8s.io/client-go/tools/cache"
25 | )
26 |
27 | // MonoVertexRolloutLister helps list MonoVertexRollouts.
28 | // All objects returned here must be treated as read-only.
29 | type MonoVertexRolloutLister interface {
30 | // List lists all MonoVertexRollouts in the indexer.
31 | // Objects returned here must be treated as read-only.
32 | List(selector labels.Selector) (ret []*v1alpha1.MonoVertexRollout, err error)
33 | // MonoVertexRollouts returns an object that can list and get MonoVertexRollouts.
34 | MonoVertexRollouts(namespace string) MonoVertexRolloutNamespaceLister
35 | MonoVertexRolloutListerExpansion
36 | }
37 |
38 | // monoVertexRolloutLister implements the MonoVertexRolloutLister interface.
39 | type monoVertexRolloutLister struct {
40 | listers.ResourceIndexer[*v1alpha1.MonoVertexRollout]
41 | }
42 |
43 | // NewMonoVertexRolloutLister returns a new MonoVertexRolloutLister.
44 | func NewMonoVertexRolloutLister(indexer cache.Indexer) MonoVertexRolloutLister {
45 | return &monoVertexRolloutLister{listers.New[*v1alpha1.MonoVertexRollout](indexer, v1alpha1.Resource("monovertexrollout"))}
46 | }
47 |
48 | // MonoVertexRollouts returns an object that can list and get MonoVertexRollouts.
49 | func (s *monoVertexRolloutLister) MonoVertexRollouts(namespace string) MonoVertexRolloutNamespaceLister {
50 | return monoVertexRolloutNamespaceLister{listers.NewNamespaced[*v1alpha1.MonoVertexRollout](s.ResourceIndexer, namespace)}
51 | }
52 |
53 | // MonoVertexRolloutNamespaceLister helps list and get MonoVertexRollouts.
54 | // All objects returned here must be treated as read-only.
55 | type MonoVertexRolloutNamespaceLister interface {
56 | // List lists all MonoVertexRollouts in the indexer for a given namespace.
57 | // Objects returned here must be treated as read-only.
58 | List(selector labels.Selector) (ret []*v1alpha1.MonoVertexRollout, err error)
59 | // Get retrieves the MonoVertexRollout from the indexer for a given namespace and name.
60 | // Objects returned here must be treated as read-only.
61 | Get(name string) (*v1alpha1.MonoVertexRollout, error)
62 | MonoVertexRolloutNamespaceListerExpansion
63 | }
64 |
65 | // monoVertexRolloutNamespaceLister implements the MonoVertexRolloutNamespaceLister
66 | // interface.
67 | type monoVertexRolloutNamespaceLister struct {
68 | listers.ResourceIndexer[*v1alpha1.MonoVertexRollout]
69 | }
70 |
--------------------------------------------------------------------------------
/internal/controller/numaflowcontroller/numaflowcontroller_controller_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package numaflowcontroller
18 |
19 | import (
20 | "fmt"
21 | "testing"
22 |
23 | "github.com/stretchr/testify/assert"
24 |
25 | apiv1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
26 | )
27 |
28 | func Test_resolveManifestTemplate(t *testing.T) {
29 | defaultInstanceID := "123"
30 |
31 | defaultController := &apiv1.NumaflowController{
32 | Spec: apiv1.NumaflowControllerSpec{
33 | InstanceID: defaultInstanceID,
34 | },
35 | }
36 |
37 | testCases := []struct {
38 | name string
39 | manifest string
40 | controller *apiv1.NumaflowController
41 | expectedManifest string
42 | expectedError error
43 | }{
44 | {
45 | name: "nil controller",
46 | manifest: "",
47 | controller: nil,
48 | expectedManifest: "",
49 | expectedError: nil,
50 | }, {
51 | name: "empty manifest",
52 | manifest: "",
53 | controller: defaultController,
54 | expectedManifest: "",
55 | expectedError: nil,
56 | }, {
57 | name: "manifest with invalid template field",
58 | manifest: "this is {{.Invalid}} invalid",
59 | controller: defaultController,
60 | expectedManifest: "",
61 | expectedError: fmt.Errorf("unable to apply information to manifest: template: manifest:1:10: executing \"manifest\" at <.Invalid>: can't evaluate field Invalid in type struct { InstanceSuffix string; InstanceID string }"),
62 | }, {
63 | name: "manifest with valid template and controller without instanceID",
64 | manifest: "valid-template-no-id{{.InstanceSuffix}}",
65 | controller: &apiv1.NumaflowController{
66 | Spec: apiv1.NumaflowControllerSpec{},
67 | },
68 | expectedManifest: "valid-template-no-id",
69 | expectedError: nil,
70 | }, {
71 | name: "manifest with valid template and controller with instanceID",
72 | manifest: "valid-template-no-id{{.InstanceSuffix}}",
73 | controller: defaultController,
74 | expectedManifest: fmt.Sprintf("valid-template-no-id-%s", defaultInstanceID),
75 | expectedError: nil,
76 | },
77 | }
78 |
79 | for _, tc := range testCases {
80 | t.Run(tc.name, func(t *testing.T) {
81 | manifestBytes, err := resolveManifestTemplate(tc.manifest, tc.controller)
82 |
83 | if tc.expectedError != nil {
84 | assert.Error(t, err)
85 | assert.EqualError(t, err, tc.expectedError.Error())
86 | } else {
87 | assert.NoError(t, err)
88 | }
89 |
90 | assert.Equal(t, tc.expectedManifest, string(manifestBytes))
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/extensions/argo/numa-rollout/numa-rollout-extension/ui/src/rollout/default/ArgoRolloutContainers.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from "react";
2 |
3 | import "./ArgoRolloutComponent.css";
4 | import { RolloutComponentContext } from "../RolloutComponentWrapper";
5 | import { Node } from "../../ArgoPropType";
6 | import {
7 | CONTROLLER_MANAGER,
8 | ISB_KUBERNETES,
9 | ISB_SERVICE_ROLLOUT,
10 | KUBERNETES_APP,
11 | MONO_VERTEX,
12 | MONOVERTEX_ROLLOUT,
13 | NUMAFLOW_CONTROLLER_ROLLOUT,
14 | PIPELINE_ROLLOUT,
15 | POD,
16 | VERTEX,
17 | } from "../../utils/Constants";
18 |
19 | export const ArgoRolloutContainers = () => {
20 | const { props } = useContext(RolloutComponentContext);
21 | const [images, setImages] = useState>(new Set());
22 | useEffect(() => {
23 | const currentNodeKind = props?.resource?.kind;
24 | // Get all pods
25 | const allPods = props?.tree?.nodes?.filter(
26 | (node: Node) => node.kind === POD
27 | );
28 | let filteredPods: any[] = [];
29 |
30 | switch (currentNodeKind) {
31 | case ISB_SERVICE_ROLLOUT:
32 | filteredPods = allPods?.filter(
33 | (pod) =>
34 | pod?.networkingInfo?.labels &&
35 | pod.networkingInfo.labels[KUBERNETES_APP] === ISB_KUBERNETES
36 | );
37 | break;
38 | case NUMAFLOW_CONTROLLER_ROLLOUT:
39 | filteredPods = allPods?.filter(
40 | (pod) =>
41 | pod?.networkingInfo?.labels &&
42 | pod.networkingInfo.labels[KUBERNETES_APP] === CONTROLLER_MANAGER
43 | );
44 | break;
45 | case PIPELINE_ROLLOUT:
46 | filteredPods = allPods?.filter(
47 | (pod) =>
48 | pod?.networkingInfo?.labels &&
49 | pod.networkingInfo.labels[KUBERNETES_APP] === VERTEX
50 | );
51 | break;
52 | case MONOVERTEX_ROLLOUT:
53 | filteredPods = allPods?.filter(
54 | (pod) =>
55 | pod?.networkingInfo?.labels &&
56 | pod.networkingInfo.labels[KUBERNETES_APP] === MONO_VERTEX
57 | );
58 | break;
59 | default:
60 | break;
61 | }
62 | let imgs: Set = new Set();
63 | filteredPods.forEach((pod) => {
64 | pod.images?.forEach((image: string) => {
65 | imgs.add(image);
66 | });
67 | });
68 | setImages(imgs);
69 | }, [props?.tree]);
70 |
71 | return (
72 |
73 |
Containers
74 | {images &&
75 | Array.from(images).map((dockerImages) => {
76 | return (
77 |
86 |
93 |
94 | );
95 | })}
96 |
97 | );
98 | };
99 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/numaflowcontroller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | "k8s.io/apimachinery/pkg/labels"
23 | "k8s.io/client-go/listers"
24 | "k8s.io/client-go/tools/cache"
25 | )
26 |
27 | // NumaflowControllerLister helps list NumaflowControllers.
28 | // All objects returned here must be treated as read-only.
29 | type NumaflowControllerLister interface {
30 | // List lists all NumaflowControllers in the indexer.
31 | // Objects returned here must be treated as read-only.
32 | List(selector labels.Selector) (ret []*v1alpha1.NumaflowController, err error)
33 | // NumaflowControllers returns an object that can list and get NumaflowControllers.
34 | NumaflowControllers(namespace string) NumaflowControllerNamespaceLister
35 | NumaflowControllerListerExpansion
36 | }
37 |
38 | // numaflowControllerLister implements the NumaflowControllerLister interface.
39 | type numaflowControllerLister struct {
40 | listers.ResourceIndexer[*v1alpha1.NumaflowController]
41 | }
42 |
43 | // NewNumaflowControllerLister returns a new NumaflowControllerLister.
44 | func NewNumaflowControllerLister(indexer cache.Indexer) NumaflowControllerLister {
45 | return &numaflowControllerLister{listers.New[*v1alpha1.NumaflowController](indexer, v1alpha1.Resource("numaflowcontroller"))}
46 | }
47 |
48 | // NumaflowControllers returns an object that can list and get NumaflowControllers.
49 | func (s *numaflowControllerLister) NumaflowControllers(namespace string) NumaflowControllerNamespaceLister {
50 | return numaflowControllerNamespaceLister{listers.NewNamespaced[*v1alpha1.NumaflowController](s.ResourceIndexer, namespace)}
51 | }
52 |
53 | // NumaflowControllerNamespaceLister helps list and get NumaflowControllers.
54 | // All objects returned here must be treated as read-only.
55 | type NumaflowControllerNamespaceLister interface {
56 | // List lists all NumaflowControllers in the indexer for a given namespace.
57 | // Objects returned here must be treated as read-only.
58 | List(selector labels.Selector) (ret []*v1alpha1.NumaflowController, err error)
59 | // Get retrieves the NumaflowController from the indexer for a given namespace and name.
60 | // Objects returned here must be treated as read-only.
61 | Get(name string) (*v1alpha1.NumaflowController, error)
62 | NumaflowControllerNamespaceListerExpansion
63 | }
64 |
65 | // numaflowControllerNamespaceLister implements the NumaflowControllerNamespaceLister
66 | // interface.
67 | type numaflowControllerNamespaceLister struct {
68 | listers.ResourceIndexer[*v1alpha1.NumaflowController]
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/pipelinerollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | "context"
22 |
23 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
24 | scheme "github.com/numaproj/numaplane/pkg/client/clientset/versioned/scheme"
25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | types "k8s.io/apimachinery/pkg/types"
27 | watch "k8s.io/apimachinery/pkg/watch"
28 | gentype "k8s.io/client-go/gentype"
29 | )
30 |
31 | // PipelineRolloutsGetter has a method to return a PipelineRolloutInterface.
32 | // A group's client should implement this interface.
33 | type PipelineRolloutsGetter interface {
34 | PipelineRollouts(namespace string) PipelineRolloutInterface
35 | }
36 |
37 | // PipelineRolloutInterface has methods to work with PipelineRollout resources.
38 | type PipelineRolloutInterface interface {
39 | Create(ctx context.Context, pipelineRollout *v1alpha1.PipelineRollout, opts v1.CreateOptions) (*v1alpha1.PipelineRollout, error)
40 | Update(ctx context.Context, pipelineRollout *v1alpha1.PipelineRollout, opts v1.UpdateOptions) (*v1alpha1.PipelineRollout, error)
41 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
42 | UpdateStatus(ctx context.Context, pipelineRollout *v1alpha1.PipelineRollout, opts v1.UpdateOptions) (*v1alpha1.PipelineRollout, error)
43 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
44 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
45 | Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PipelineRollout, error)
46 | List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PipelineRolloutList, error)
47 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
48 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PipelineRollout, err error)
49 | PipelineRolloutExpansion
50 | }
51 |
52 | // pipelineRollouts implements PipelineRolloutInterface
53 | type pipelineRollouts struct {
54 | *gentype.ClientWithList[*v1alpha1.PipelineRollout, *v1alpha1.PipelineRolloutList]
55 | }
56 |
57 | // newPipelineRollouts returns a PipelineRollouts
58 | func newPipelineRollouts(c *NumaplaneV1alpha1Client, namespace string) *pipelineRollouts {
59 | return &pipelineRollouts{
60 | gentype.NewClientWithList[*v1alpha1.PipelineRollout, *v1alpha1.PipelineRolloutList](
61 | "pipelinerollouts",
62 | c.RESTClient(),
63 | scheme.ParameterCodec,
64 | namespace,
65 | func() *v1alpha1.PipelineRollout { return &v1alpha1.PipelineRollout{} },
66 | func() *v1alpha1.PipelineRolloutList { return &v1alpha1.PipelineRolloutList{} }),
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/pkg/client/informers/externalversions/numaplane/v1alpha1/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by informer-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | internalinterfaces "github.com/numaproj/numaplane/pkg/client/informers/externalversions/internalinterfaces"
22 | )
23 |
24 | // Interface provides access to all the informers in this group version.
25 | type Interface interface {
26 | // ISBServiceRollouts returns a ISBServiceRolloutInformer.
27 | ISBServiceRollouts() ISBServiceRolloutInformer
28 | // MonoVertexRollouts returns a MonoVertexRolloutInformer.
29 | MonoVertexRollouts() MonoVertexRolloutInformer
30 | // NumaflowControllers returns a NumaflowControllerInformer.
31 | NumaflowControllers() NumaflowControllerInformer
32 | // NumaflowControllerRollouts returns a NumaflowControllerRolloutInformer.
33 | NumaflowControllerRollouts() NumaflowControllerRolloutInformer
34 | // PipelineRollouts returns a PipelineRolloutInformer.
35 | PipelineRollouts() PipelineRolloutInformer
36 | }
37 |
38 | type version struct {
39 | factory internalinterfaces.SharedInformerFactory
40 | namespace string
41 | tweakListOptions internalinterfaces.TweakListOptionsFunc
42 | }
43 |
44 | // New returns a new Interface.
45 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
46 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
47 | }
48 |
49 | // ISBServiceRollouts returns a ISBServiceRolloutInformer.
50 | func (v *version) ISBServiceRollouts() ISBServiceRolloutInformer {
51 | return &iSBServiceRolloutInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
52 | }
53 |
54 | // MonoVertexRollouts returns a MonoVertexRolloutInformer.
55 | func (v *version) MonoVertexRollouts() MonoVertexRolloutInformer {
56 | return &monoVertexRolloutInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
57 | }
58 |
59 | // NumaflowControllers returns a NumaflowControllerInformer.
60 | func (v *version) NumaflowControllers() NumaflowControllerInformer {
61 | return &numaflowControllerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
62 | }
63 |
64 | // NumaflowControllerRollouts returns a NumaflowControllerRolloutInformer.
65 | func (v *version) NumaflowControllerRollouts() NumaflowControllerRolloutInformer {
66 | return &numaflowControllerRolloutInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
67 | }
68 |
69 | // PipelineRollouts returns a PipelineRolloutInformer.
70 | func (v *version) PipelineRollouts() PipelineRolloutInformer {
71 | return &pipelineRolloutInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
72 | }
73 |
--------------------------------------------------------------------------------
/internal/util/maps.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import (
20 | "maps"
21 | "strings"
22 |
23 | "github.com/google/go-cmp/cmp"
24 | )
25 |
26 | func MergeMaps(existing, new map[string]string) map[string]string {
27 | merged := make(map[string]string)
28 | if existing != nil {
29 | merged = existing
30 | }
31 |
32 | for key, val := range new {
33 | merged[key] = val
34 | }
35 |
36 | return merged
37 | }
38 |
39 | func CompareMaps(existing, new map[string]string) bool {
40 | if existing == nil || new == nil {
41 | return len(existing) == len(new)
42 | }
43 | return cmp.Equal(existing, new)
44 | }
45 |
46 | // ConvertInterfaceMapToStringMap converts a map[string]interface{} to map[string]string
47 | // by type asserting each value to string. Non-string values are skipped.
48 | func ConvertInterfaceMapToStringMap(interfaceMap map[string]interface{}) map[string]string {
49 | if interfaceMap == nil {
50 | return nil
51 | }
52 |
53 | stringMap := make(map[string]string)
54 | for k, v := range interfaceMap {
55 | if str, ok := v.(string); ok {
56 | stringMap[k] = str
57 | }
58 | }
59 | return stringMap
60 | }
61 |
62 | func IsMapSubset(requiredKVPairs map[string]string, mapToCheck map[string]string) bool {
63 |
64 | // If there are no required key-value pairs (nil or empty), always return true
65 | if len(requiredKVPairs) == 0 {
66 | return true
67 | }
68 | // If the map to look for is nil or empty, return false
69 | if len(mapToCheck) == 0 {
70 | return false
71 | }
72 |
73 | for key, value := range requiredKVPairs {
74 | if mapToCheck[key] != value {
75 | return false
76 | }
77 | }
78 | return true
79 | }
80 |
81 | // CompareMapsWithExceptions compares two maps but ignoring any differences where the keys are prefixed with any of the 'prefixExceptions'
82 | func CompareMapsWithExceptions(existing, new map[string]string, prefixExceptions ...string) bool {
83 | // clone the maps because we can make nil maps empty maps to make it easier to compare
84 | existingCopy := maps.Clone(existing)
85 | newCopy := maps.Clone(new)
86 | if existingCopy == nil {
87 | existingCopy = make(map[string]string)
88 | }
89 | if newCopy == nil {
90 | newCopy = make(map[string]string)
91 | }
92 |
93 | for existingKey, existingValue := range existingCopy {
94 | isException := false
95 | for _, prefixException := range prefixExceptions {
96 | if strings.HasPrefix(existingKey, prefixException) {
97 | isException = true
98 | break
99 | }
100 | }
101 | if !isException {
102 | // is this key in the other map and does it have the same value?
103 | newValue := newCopy[existingKey]
104 | if existingValue != newValue {
105 | return false
106 | }
107 | }
108 |
109 | }
110 |
111 | return true
112 | }
113 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/isbservicerollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | "context"
22 |
23 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
24 | scheme "github.com/numaproj/numaplane/pkg/client/clientset/versioned/scheme"
25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | types "k8s.io/apimachinery/pkg/types"
27 | watch "k8s.io/apimachinery/pkg/watch"
28 | gentype "k8s.io/client-go/gentype"
29 | )
30 |
31 | // ISBServiceRolloutsGetter has a method to return a ISBServiceRolloutInterface.
32 | // A group's client should implement this interface.
33 | type ISBServiceRolloutsGetter interface {
34 | ISBServiceRollouts(namespace string) ISBServiceRolloutInterface
35 | }
36 |
37 | // ISBServiceRolloutInterface has methods to work with ISBServiceRollout resources.
38 | type ISBServiceRolloutInterface interface {
39 | Create(ctx context.Context, iSBServiceRollout *v1alpha1.ISBServiceRollout, opts v1.CreateOptions) (*v1alpha1.ISBServiceRollout, error)
40 | Update(ctx context.Context, iSBServiceRollout *v1alpha1.ISBServiceRollout, opts v1.UpdateOptions) (*v1alpha1.ISBServiceRollout, error)
41 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
42 | UpdateStatus(ctx context.Context, iSBServiceRollout *v1alpha1.ISBServiceRollout, opts v1.UpdateOptions) (*v1alpha1.ISBServiceRollout, error)
43 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
44 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
45 | Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ISBServiceRollout, error)
46 | List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ISBServiceRolloutList, error)
47 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
48 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ISBServiceRollout, err error)
49 | ISBServiceRolloutExpansion
50 | }
51 |
52 | // iSBServiceRollouts implements ISBServiceRolloutInterface
53 | type iSBServiceRollouts struct {
54 | *gentype.ClientWithList[*v1alpha1.ISBServiceRollout, *v1alpha1.ISBServiceRolloutList]
55 | }
56 |
57 | // newISBServiceRollouts returns a ISBServiceRollouts
58 | func newISBServiceRollouts(c *NumaplaneV1alpha1Client, namespace string) *iSBServiceRollouts {
59 | return &iSBServiceRollouts{
60 | gentype.NewClientWithList[*v1alpha1.ISBServiceRollout, *v1alpha1.ISBServiceRolloutList](
61 | "isbservicerollouts",
62 | c.RESTClient(),
63 | scheme.ParameterCodec,
64 | namespace,
65 | func() *v1alpha1.ISBServiceRollout { return &v1alpha1.ISBServiceRollout{} },
66 | func() *v1alpha1.ISBServiceRolloutList { return &v1alpha1.ISBServiceRolloutList{} }),
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/monovertexrollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | "context"
22 |
23 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
24 | scheme "github.com/numaproj/numaplane/pkg/client/clientset/versioned/scheme"
25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | types "k8s.io/apimachinery/pkg/types"
27 | watch "k8s.io/apimachinery/pkg/watch"
28 | gentype "k8s.io/client-go/gentype"
29 | )
30 |
31 | // MonoVertexRolloutsGetter has a method to return a MonoVertexRolloutInterface.
32 | // A group's client should implement this interface.
33 | type MonoVertexRolloutsGetter interface {
34 | MonoVertexRollouts(namespace string) MonoVertexRolloutInterface
35 | }
36 |
37 | // MonoVertexRolloutInterface has methods to work with MonoVertexRollout resources.
38 | type MonoVertexRolloutInterface interface {
39 | Create(ctx context.Context, monoVertexRollout *v1alpha1.MonoVertexRollout, opts v1.CreateOptions) (*v1alpha1.MonoVertexRollout, error)
40 | Update(ctx context.Context, monoVertexRollout *v1alpha1.MonoVertexRollout, opts v1.UpdateOptions) (*v1alpha1.MonoVertexRollout, error)
41 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
42 | UpdateStatus(ctx context.Context, monoVertexRollout *v1alpha1.MonoVertexRollout, opts v1.UpdateOptions) (*v1alpha1.MonoVertexRollout, error)
43 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
44 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
45 | Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.MonoVertexRollout, error)
46 | List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.MonoVertexRolloutList, error)
47 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
48 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.MonoVertexRollout, err error)
49 | MonoVertexRolloutExpansion
50 | }
51 |
52 | // monoVertexRollouts implements MonoVertexRolloutInterface
53 | type monoVertexRollouts struct {
54 | *gentype.ClientWithList[*v1alpha1.MonoVertexRollout, *v1alpha1.MonoVertexRolloutList]
55 | }
56 |
57 | // newMonoVertexRollouts returns a MonoVertexRollouts
58 | func newMonoVertexRollouts(c *NumaplaneV1alpha1Client, namespace string) *monoVertexRollouts {
59 | return &monoVertexRollouts{
60 | gentype.NewClientWithList[*v1alpha1.MonoVertexRollout, *v1alpha1.MonoVertexRolloutList](
61 | "monovertexrollouts",
62 | c.RESTClient(),
63 | scheme.ParameterCodec,
64 | namespace,
65 | func() *v1alpha1.MonoVertexRollout { return &v1alpha1.MonoVertexRollout{} },
66 | func() *v1alpha1.MonoVertexRolloutList { return &v1alpha1.MonoVertexRolloutList{} }),
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/internal/controller/common/numaflowtypes/isbsvc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package numaflowtypes
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | numaflowv1 "github.com/numaproj/numaflow/pkg/apis/numaflow/v1alpha1"
24 | "github.com/numaproj/numaplane/internal/util/kubernetes"
25 | appsv1 "k8s.io/api/apps/v1"
26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 | "k8s.io/apimachinery/pkg/labels"
29 | "k8s.io/apimachinery/pkg/selection"
30 | "sigs.k8s.io/controller-runtime/pkg/client"
31 | )
32 |
33 | // Each ISBService has one underlying StatefulSet
34 | // Find it
35 | // Depending on value "checkLive", either check K8S API directly or go to informer cache
36 | func GetISBSvcStatefulSetFromK8s(ctx context.Context, c client.Client, isbsvc *unstructured.Unstructured, checkLive bool) (*appsv1.StatefulSet, error) {
37 | statefulSetSelector := labels.NewSelector()
38 | requirement, err := labels.NewRequirement(numaflowv1.KeyISBSvcName, selection.Equals, []string{isbsvc.GetName()})
39 | if err != nil {
40 | return nil, fmt.Errorf("Error creating label requirement: %v", err)
41 | }
42 | statefulSetSelector = statefulSetSelector.Add(*requirement)
43 |
44 | var statefulSetList appsv1.StatefulSetList
45 | if checkLive {
46 | statefulSets, err := kubernetes.KubernetesClient.AppsV1().StatefulSets(isbsvc.GetNamespace()).List(ctx, metav1.ListOptions{
47 | LabelSelector: fmt.Sprintf("%s=%s", numaflowv1.KeyISBSvcName, isbsvc.GetName())})
48 | if err != nil {
49 | return nil, fmt.Errorf("Error listing live StatefulSets: %v", err)
50 | }
51 | statefulSetList = *statefulSets
52 | } else {
53 | //TODO: this is currently making a Live K8S call, not a call to cache as it's supposed to
54 | // Option 1: figure out how we can watch a StatefulSet and have its owner's owner reconcile it
55 | // Option 2: update InterstepBufferService code in Numaflow to provide all the info we need so we don't need to access StatefulSet directly
56 | err = c.List(ctx, &statefulSetList, &client.ListOptions{Namespace: isbsvc.GetNamespace(), LabelSelector: statefulSetSelector})
57 | if err != nil {
58 | return nil, fmt.Errorf("Error listing StatefulSets: %v", err)
59 | }
60 | }
61 | if len(statefulSetList.Items) > 1 {
62 | return nil, fmt.Errorf("unexpected: isbsvc %s/%s has multiple StatefulSets: %+v", isbsvc.GetNamespace(), isbsvc.GetName(), statefulSetList.Items)
63 | } else if len(statefulSetList.Items) == 0 {
64 | return nil, nil
65 | } else {
66 | return &(statefulSetList.Items[0]), nil
67 | }
68 | }
69 |
70 | func GetISBServiceChildResourceHealth(conditions []metav1.Condition) (metav1.ConditionStatus, string) {
71 | for _, cond := range conditions {
72 | if cond.Type == "ChildrenResourcesHealthy" && cond.Status != metav1.ConditionTrue {
73 | return cond.Status, cond.Reason
74 | }
75 | }
76 | return metav1.ConditionTrue, ""
77 | }
78 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/numaflowcontroller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | "context"
22 |
23 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
24 | scheme "github.com/numaproj/numaplane/pkg/client/clientset/versioned/scheme"
25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | types "k8s.io/apimachinery/pkg/types"
27 | watch "k8s.io/apimachinery/pkg/watch"
28 | gentype "k8s.io/client-go/gentype"
29 | )
30 |
31 | // NumaflowControllersGetter has a method to return a NumaflowControllerInterface.
32 | // A group's client should implement this interface.
33 | type NumaflowControllersGetter interface {
34 | NumaflowControllers(namespace string) NumaflowControllerInterface
35 | }
36 |
37 | // NumaflowControllerInterface has methods to work with NumaflowController resources.
38 | type NumaflowControllerInterface interface {
39 | Create(ctx context.Context, numaflowController *v1alpha1.NumaflowController, opts v1.CreateOptions) (*v1alpha1.NumaflowController, error)
40 | Update(ctx context.Context, numaflowController *v1alpha1.NumaflowController, opts v1.UpdateOptions) (*v1alpha1.NumaflowController, error)
41 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
42 | UpdateStatus(ctx context.Context, numaflowController *v1alpha1.NumaflowController, opts v1.UpdateOptions) (*v1alpha1.NumaflowController, error)
43 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
44 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
45 | Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NumaflowController, error)
46 | List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.NumaflowControllerList, error)
47 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
48 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.NumaflowController, err error)
49 | NumaflowControllerExpansion
50 | }
51 |
52 | // numaflowControllers implements NumaflowControllerInterface
53 | type numaflowControllers struct {
54 | *gentype.ClientWithList[*v1alpha1.NumaflowController, *v1alpha1.NumaflowControllerList]
55 | }
56 |
57 | // newNumaflowControllers returns a NumaflowControllers
58 | func newNumaflowControllers(c *NumaplaneV1alpha1Client, namespace string) *numaflowControllers {
59 | return &numaflowControllers{
60 | gentype.NewClientWithList[*v1alpha1.NumaflowController, *v1alpha1.NumaflowControllerList](
61 | "numaflowcontrollers",
62 | c.RESTClient(),
63 | scheme.ParameterCodec,
64 | namespace,
65 | func() *v1alpha1.NumaflowController { return &v1alpha1.NumaflowController{} },
66 | func() *v1alpha1.NumaflowControllerList { return &v1alpha1.NumaflowControllerList{} }),
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/pkg/client/clientset/versioned/fake/clientset_generated.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by client-gen. DO NOT EDIT.
17 |
18 | package fake
19 |
20 | import (
21 | clientset "github.com/numaproj/numaplane/pkg/client/clientset/versioned"
22 | numaplanev1alpha1 "github.com/numaproj/numaplane/pkg/client/clientset/versioned/typed/numaplane/v1alpha1"
23 | fakenumaplanev1alpha1 "github.com/numaproj/numaplane/pkg/client/clientset/versioned/typed/numaplane/v1alpha1/fake"
24 | "k8s.io/apimachinery/pkg/runtime"
25 | "k8s.io/apimachinery/pkg/watch"
26 | "k8s.io/client-go/discovery"
27 | fakediscovery "k8s.io/client-go/discovery/fake"
28 | "k8s.io/client-go/testing"
29 | )
30 |
31 | // NewSimpleClientset returns a clientset that will respond with the provided objects.
32 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
33 | // without applying any field management, validations and/or defaults. It shouldn't be considered a replacement
34 | // for a real clientset and is mostly useful in simple unit tests.
35 | //
36 | // DEPRECATED: NewClientset replaces this with support for field management, which significantly improves
37 | // server side apply testing. NewClientset is only available when apply configurations are generated (e.g.
38 | // via --with-applyconfig).
39 | func NewSimpleClientset(objects ...runtime.Object) *Clientset {
40 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
41 | for _, obj := range objects {
42 | if err := o.Add(obj); err != nil {
43 | panic(err)
44 | }
45 | }
46 |
47 | cs := &Clientset{tracker: o}
48 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
49 | cs.AddReactor("*", "*", testing.ObjectReaction(o))
50 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
51 | gvr := action.GetResource()
52 | ns := action.GetNamespace()
53 | watch, err := o.Watch(gvr, ns)
54 | if err != nil {
55 | return false, nil, err
56 | }
57 | return true, watch, nil
58 | })
59 |
60 | return cs
61 | }
62 |
63 | // Clientset implements clientset.Interface. Meant to be embedded into a
64 | // struct to get a default implementation. This makes faking out just the method
65 | // you want to test easier.
66 | type Clientset struct {
67 | testing.Fake
68 | discovery *fakediscovery.FakeDiscovery
69 | tracker testing.ObjectTracker
70 | }
71 |
72 | func (c *Clientset) Discovery() discovery.DiscoveryInterface {
73 | return c.discovery
74 | }
75 |
76 | func (c *Clientset) Tracker() testing.ObjectTracker {
77 | return c.tracker
78 | }
79 |
80 | var (
81 | _ clientset.Interface = &Clientset{}
82 | _ testing.FakeClient = &Clientset{}
83 | )
84 |
85 | // NumaplaneV1alpha1 retrieves the NumaplaneV1alpha1Client
86 | func (c *Clientset) NumaplaneV1alpha1() numaplanev1alpha1.NumaplaneV1alpha1Interface {
87 | return &fakenumaplanev1alpha1.FakeNumaplaneV1alpha1{Fake: &c.Fake}
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/client/listers/numaplane/v1alpha1/numaflowcontrollerrollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | // Code generated by lister-gen. DO NOT EDIT.
17 |
18 | package v1alpha1
19 |
20 | import (
21 | v1alpha1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
22 | "k8s.io/apimachinery/pkg/labels"
23 | "k8s.io/client-go/listers"
24 | "k8s.io/client-go/tools/cache"
25 | )
26 |
27 | // NumaflowControllerRolloutLister helps list NumaflowControllerRollouts.
28 | // All objects returned here must be treated as read-only.
29 | type NumaflowControllerRolloutLister interface {
30 | // List lists all NumaflowControllerRollouts in the indexer.
31 | // Objects returned here must be treated as read-only.
32 | List(selector labels.Selector) (ret []*v1alpha1.NumaflowControllerRollout, err error)
33 | // NumaflowControllerRollouts returns an object that can list and get NumaflowControllerRollouts.
34 | NumaflowControllerRollouts(namespace string) NumaflowControllerRolloutNamespaceLister
35 | NumaflowControllerRolloutListerExpansion
36 | }
37 |
38 | // numaflowControllerRolloutLister implements the NumaflowControllerRolloutLister interface.
39 | type numaflowControllerRolloutLister struct {
40 | listers.ResourceIndexer[*v1alpha1.NumaflowControllerRollout]
41 | }
42 |
43 | // NewNumaflowControllerRolloutLister returns a new NumaflowControllerRolloutLister.
44 | func NewNumaflowControllerRolloutLister(indexer cache.Indexer) NumaflowControllerRolloutLister {
45 | return &numaflowControllerRolloutLister{listers.New[*v1alpha1.NumaflowControllerRollout](indexer, v1alpha1.Resource("numaflowcontrollerrollout"))}
46 | }
47 |
48 | // NumaflowControllerRollouts returns an object that can list and get NumaflowControllerRollouts.
49 | func (s *numaflowControllerRolloutLister) NumaflowControllerRollouts(namespace string) NumaflowControllerRolloutNamespaceLister {
50 | return numaflowControllerRolloutNamespaceLister{listers.NewNamespaced[*v1alpha1.NumaflowControllerRollout](s.ResourceIndexer, namespace)}
51 | }
52 |
53 | // NumaflowControllerRolloutNamespaceLister helps list and get NumaflowControllerRollouts.
54 | // All objects returned here must be treated as read-only.
55 | type NumaflowControllerRolloutNamespaceLister interface {
56 | // List lists all NumaflowControllerRollouts in the indexer for a given namespace.
57 | // Objects returned here must be treated as read-only.
58 | List(selector labels.Selector) (ret []*v1alpha1.NumaflowControllerRollout, err error)
59 | // Get retrieves the NumaflowControllerRollout from the indexer for a given namespace and name.
60 | // Objects returned here must be treated as read-only.
61 | Get(name string) (*v1alpha1.NumaflowControllerRollout, error)
62 | NumaflowControllerRolloutNamespaceListerExpansion
63 | }
64 |
65 | // numaflowControllerRolloutNamespaceLister implements the NumaflowControllerRolloutNamespaceLister
66 | // interface.
67 | type numaflowControllerRolloutNamespaceLister struct {
68 | listers.ResourceIndexer[*v1alpha1.NumaflowControllerRollout]
69 | }
70 |
--------------------------------------------------------------------------------
/internal/controller/common/in_progress_strategy_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package common
18 |
19 | import (
20 | "context"
21 | "testing"
22 |
23 | "github.com/stretchr/testify/assert"
24 |
25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | k8stypes "k8s.io/apimachinery/pkg/types"
27 | "sigs.k8s.io/controller-runtime/pkg/client"
28 |
29 | apiv1 "github.com/numaproj/numaplane/pkg/apis/numaplane/v1alpha1"
30 | )
31 |
32 | func Test_inProgressStrategyMgr_getStrategy(t *testing.T) {
33 |
34 | progressiveStrategy := apiv1.UpgradeStrategyProgressive
35 | ppndStrategy := apiv1.UpgradeStrategyPPND
36 | noStrategy := apiv1.UpgradeStrategyNoOp
37 |
38 | testCases := []struct {
39 | name string
40 | inMemoryStrategy *apiv1.UpgradeStrategy
41 | rolloutStatusStrategy *apiv1.UpgradeStrategy
42 | resultStrategy apiv1.UpgradeStrategy
43 | }{
44 | {
45 | name: "in memory and in Rollout Status (progressive result)",
46 | inMemoryStrategy: &progressiveStrategy,
47 | rolloutStatusStrategy: &noStrategy,
48 | resultStrategy: progressiveStrategy,
49 | },
50 | {
51 | name: "in memory and in Rollout Status (no op result)",
52 | inMemoryStrategy: &noStrategy,
53 | rolloutStatusStrategy: &ppndStrategy,
54 | resultStrategy: noStrategy,
55 | },
56 | {
57 | name: "in memory and not in Rollout Status",
58 | inMemoryStrategy: &progressiveStrategy,
59 | rolloutStatusStrategy: nil,
60 | resultStrategy: progressiveStrategy,
61 | },
62 | {
63 | name: "in Rollout Status and not in memory",
64 | inMemoryStrategy: nil,
65 | rolloutStatusStrategy: &ppndStrategy,
66 | resultStrategy: ppndStrategy,
67 | },
68 | {
69 | name: "neither in Rollout Status nor in memory",
70 | inMemoryStrategy: nil,
71 | rolloutStatusStrategy: nil,
72 | resultStrategy: noStrategy,
73 | },
74 | }
75 |
76 | for _, tc := range testCases {
77 | t.Run(tc.name, func(t *testing.T) {
78 | inProgressStrategyMgr := NewInProgressStrategyMgr(
79 | // getRolloutStrategy function:
80 | func(ctx context.Context, rollout client.Object) *apiv1.UpgradeStrategy {
81 | return tc.rolloutStatusStrategy
82 | },
83 | // setRolloutStrategy function:
84 | func(ctx context.Context, rollout client.Object, strategy apiv1.UpgradeStrategy) {},
85 | )
86 | pipelineRollout := &apiv1.PipelineRollout{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "my-pipeline"}}
87 | namespacedName := k8stypes.NamespacedName{Namespace: pipelineRollout.GetNamespace(), Name: pipelineRollout.GetName()}
88 | if tc.inMemoryStrategy != nil {
89 | inProgressStrategyMgr.Store.SetStrategy(namespacedName, *tc.inMemoryStrategy)
90 | }
91 | upgradeStrategyResult := inProgressStrategyMgr.GetStrategy(context.Background(), pipelineRollout)
92 | assert.Equal(t, tc.resultStrategy, upgradeStrategyResult)
93 | })
94 | }
95 | }
96 |
--------------------------------------------------------------------------------