├── docs ├── .placeholder ├── README.md ├── releases.md ├── installation-macos.md ├── development.md └── oadp-backups.md ├── playbooks └── .placeholder ├── roles └── tackle │ ├── files │ └── .placeholder │ ├── templates │ ├── configmap-trusted-ca.yml.j2 │ ├── kai │ │ ├── kai-db-service.yaml.j2 │ │ ├── kai-api-service.yaml.j2 │ │ ├── kai-db-pvc.yaml.j2 │ │ ├── llm-proxy-service.yaml.j2 │ │ ├── llm-proxy-client-configmap.yaml.j2 │ │ ├── kai-api-deployment.yaml.j2 │ │ ├── kai-db-deployment.yaml.j2 │ │ ├── llm-proxy-deployment.yaml.j2 │ │ └── llm-proxy-configmap.yaml.j2 │ ├── serviceaccount-ui.yml.j2 │ ├── service-keycloak-rhbk.yml.j2 │ ├── customresource-rhsso-keycloak.yml.j2 │ ├── secret-cookie-secret.yml.j2 │ ├── secret-hub.yml.j2 │ ├── secret-keycloak-sso.yml.j2 │ ├── secret-keycloak-postgresql.yml.j2 │ ├── servicemonitor-hub.yml.j2 │ ├── persistentvolumeclaim-cache.yml.j2 │ ├── secret-keycloak-db.yml.j2 │ ├── persistentvolumeclaim-hub-database.yml.j2 │ ├── persistentvolumeclaim-hub-bucket.yml.j2 │ ├── route-ui.yml.j2 │ ├── persistentvolumeclaim-keycloak-postgresql.yml.j2 │ ├── service-keycloak-postgresql.yml.j2 │ ├── service-keycloak-postgresql-migration.yml.j2 │ ├── service-hub.yml.j2 │ ├── service-keycloak-sso.yml.j2 │ ├── service-ui.yml.j2 │ ├── customresource-rhbk-keycloak.yml.j2 │ ├── ingress-ui.yml.j2 │ ├── networkpolicy.yml.j2 │ ├── customresource-task.yml.j2 │ ├── customresource-addon.yml.j2 │ ├── customresource-schema.yml.j2 │ ├── deployment-keycloak-postgresql.yml.j2 │ ├── deployment-keycloak-sso.yml.j2 │ ├── customresource-extension.yml.j2 │ ├── deployment-ui.yml.j2 │ └── deployment-hub.yml.j2 │ ├── meta │ └── main.yml │ └── tasks │ ├── kai.yml │ └── kai-status.yml ├── helm ├── templates │ ├── rbac │ │ ├── ui_service_account.yaml │ │ ├── hub_service_account.yaml │ │ ├── service_account.yaml │ │ ├── role_binding.yaml │ │ ├── ui_cluster_role.yaml │ │ ├── hub_role_binding.yaml │ │ ├── cluster_role_binding.yaml │ │ ├── ui_cluster_role_binding.yaml │ │ ├── hub_role.yaml │ │ ├── cluster_role.yaml │ │ └── role.yaml │ ├── crds │ │ ├── tackle.konveyor.io_tackles.sample.yaml │ │ ├── tackle.konveyor.io_tasks.sample.yaml │ │ ├── tackle.konveyor.io_addons.sample.yaml │ │ ├── tackle.konveyor.io_extensions.sample.yaml │ │ ├── tackle.konveyor.io_schemas.samples.yaml │ │ ├── tackle.konveyor.io_tackles.yaml │ │ ├── tackle.konveyor.io_tasks.yaml │ │ └── tackle.konveyor.io_schemas.yaml │ ├── olm │ │ ├── scorecard.yaml │ │ └── konveyor-operator.clusterserviceversion.yaml │ └── deployment.yaml ├── .helmignore ├── Chart.yaml └── values.yaml ├── molecule ├── default │ ├── create.yml │ ├── converge.yml │ ├── tasks │ │ └── tackle_test.yml │ ├── destroy.yml │ ├── kustomize.yml │ ├── molecule.yml │ ├── prepare.yml │ └── verify.yml └── kind │ ├── create.yml │ ├── destroy.yml │ ├── converge.yml │ └── molecule.yml ├── .ansible-lint-ignore ├── watches.yaml ├── .config └── ansible-lint.yml ├── .gitignore ├── requirements.yml ├── .github ├── workflows │ ├── pr-title-check.yml │ ├── pr-closed.yaml │ ├── csv-validate.yml │ ├── issues.yaml │ ├── pr-ci.yml │ ├── stale.yml │ └── march-image-build-push.yml └── actions │ ├── start-minikube │ └── action.yml │ ├── install-konveyor │ └── action.yml │ ├── install-tackle │ └── action.yml │ └── make-bundle │ └── action.yml ├── konveyor-operator-catalog.yaml ├── PROJECT ├── tackle-k8s-dev.yaml ├── tools └── upgrades │ ├── jwt.sh │ └── migrate-pathfinder-assessments.py ├── bundle ├── metadata │ └── annotations.yaml ├── tests │ └── scorecard │ │ └── config.yaml └── manifests │ ├── tackle.konveyor.io_tackles.yaml │ ├── tackle.konveyor.io_tasks.yaml │ └── tackle.konveyor.io_schemas.yaml ├── tackle-k8s.yaml ├── Dockerfile ├── bundle.Dockerfile ├── hack ├── start-minikube.sh ├── install-konveyor.sh └── install-tackle.sh └── LICENSE /docs/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playbooks/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /roles/tackle/files/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /helm/templates/rbac/ui_service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tackle-ui 5 | -------------------------------------------------------------------------------- /helm/templates/rbac/hub_service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tackle-hub 5 | -------------------------------------------------------------------------------- /helm/templates/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tackle-operator 5 | -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: [] 7 | -------------------------------------------------------------------------------- /.ansible-lint-ignore: -------------------------------------------------------------------------------- 1 | # This file contains ignores rule violations for ansible-lint 2 | roles/tackle/tasks/main.yml var-naming[no-role-prefix] 3 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Konveyor Tackle Operator Documentation 2 | 3 | ## Upstream Release Instructions 4 | See [releases.md](./releases.md) for details. 5 | 6 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use the 'create api' subcommand to add watches to this file. 3 | - version: v1alpha1 4 | group: tackle.konveyor.io 5 | kind: Tackle 6 | role: tackle 7 | #+kubebuilder:scaffold:watch 8 | -------------------------------------------------------------------------------- /.config/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | # Ansible linter configuration 2 | 3 | warn_list: # or 'skip_list' to silence them completely 4 | - yaml 5 | 6 | skip_list: 7 | - experimental 8 | - fqcn 9 | - unnamed-task 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin 8 | 9 | # editor and IDE paraphernalia 10 | .idea 11 | *.swp 12 | *.swo 13 | *~ 14 | 15 | # other 16 | TODO 17 | -------------------------------------------------------------------------------- /molecule/kind/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: 7 | - name: Create test kind cluster 8 | command: kind create cluster --name osdk-test --kubeconfig {{ kubeconfig }} 9 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_tackles.sample.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: tackle.konveyor.io/v1alpha1 3 | kind: Tackle 4 | metadata: 5 | name: tackle 6 | namespace: konveyor-tackle 7 | spec: 8 | feature_auth_required: 'false' 9 | {{ end }} 10 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_tasks.sample.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: tackle.konveyor.io/v1alpha1 3 | kind: Task 4 | metadata: 5 | name: mytask 6 | namespace: konveyor-tackle 7 | spec: 8 | dependencies: 9 | - myaddon 10 | priority: 10 11 | {{ end }} 12 | -------------------------------------------------------------------------------- /roles/tackle/templates/configmap-trusted-ca.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: trusted-ca 6 | namespace: {{ app_namespace }} 7 | labels: 8 | config.openshift.io/inject-trusted-cabundle: "true" 9 | app.kubernetes.io/part-of: {{ app_name }} 10 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/kai-db-service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: kai-db 6 | namespace: "{{ app_namespace }}" 7 | spec: 8 | selector: 9 | app: kai-db 10 | ports: 11 | - protocol: TCP 12 | port: 5432 13 | targetPort: 5432 14 | -------------------------------------------------------------------------------- /helm/templates/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: tackle-operator 12 | -------------------------------------------------------------------------------- /helm/templates/rbac/ui_cluster_role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: tackle-ui-role 6 | rules: [] # Add clusterRules for tackle-ui SA in CSV if not empty, right now it's empty, so CSV contains no mention of tackle-ui SA 7 | #+kubebuilder:scaffold:rules 8 | -------------------------------------------------------------------------------- /helm/templates/rbac/hub_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: tackle-hub-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: tackle-hub-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: tackle-hub 12 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/kai-api-service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ kai_service_name }}" 6 | namespace: "{{ app_namespace }}" 7 | spec: 8 | selector: 9 | app: kai-api 10 | ports: 11 | - protocol: TCP 12 | port: 8000 13 | targetPort: 8000 14 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: operator_sdk.util 4 | version: "0.5.0" 5 | - name: kubernetes.core 6 | version: "2.4.0" 7 | - name: cloud.common 8 | version: "2.1.1" 9 | - name: community.general 10 | version: "4.8.11" 11 | - name: community.postgresql 12 | version: "3.10.2" 13 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/kai-db-pvc.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: "{{ kai_database_volume_claim_name }}" 6 | namespace: "{{ app_namespace }}" 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: "{{ kai_database_volume_size }}" 13 | -------------------------------------------------------------------------------- /.github/workflows/pr-title-check.yml: -------------------------------------------------------------------------------- 1 | name: PR Title Check 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited, reopened, synchronize] 6 | 7 | jobs: 8 | verify-pr: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: konveyor/release-tools/cmd/verify-pr@main 12 | with: 13 | github_token: ${{ secrets.GITHUB_TOKEN }} 14 | -------------------------------------------------------------------------------- /roles/tackle/templates/serviceaccount-ui.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ ui_serviceaccount_name }} 5 | namespace: {{ app_namespace }} 6 | annotations: 7 | serviceaccounts.openshift.io/oauth-redirectreference.primary: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"{{ ui_route_name }}"}}' 8 | -------------------------------------------------------------------------------- /helm/templates/rbac/cluster_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: tackle-operator 12 | namespace: {{ .Release.Namespace }} 13 | -------------------------------------------------------------------------------- /helm/templates/rbac/ui_cluster_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: tackle-ui-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: tackle-ui-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: tackle-ui 12 | namespace: {{ .Release.Namespace }} 13 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-keycloak-rhbk.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | {% if keycloak_sso_tls_enabled|bool and openshift_cluster|bool %} 6 | annotations: 7 | service.beta.openshift.io/serving-cert-secret-name: {{ rhbk_tls_secret_name }} 8 | {% endif %} 9 | name: {{ rhbk_service_name }}-service 10 | namespace: {{ app_namespace }} 11 | -------------------------------------------------------------------------------- /konveyor-operator-catalog.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: CatalogSource 3 | metadata: 4 | name: konveyor 5 | namespace: openshift-marketplace 6 | spec: 7 | displayName: Konveyor Operator 8 | publisher: Konveyor 9 | sourceType: grpc 10 | image: quay.io/konveyor/tackle2-operator-index:latest 11 | updateStrategy: 12 | registryPoll: 13 | interval: 10m 14 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: quay.io 2 | layout: 3 | - ansible.sdk.operatorframework.io/v1 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: konveyor-operator 8 | resources: 9 | - api: 10 | crdVersion: v1 11 | namespaced: true 12 | domain: konveyor.io 13 | group: tackle 14 | kind: Tackle 15 | version: v1alpha1 16 | version: "3" 17 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - name: Create Namespace 11 | k8s: 12 | api_version: v1 13 | kind: Namespace 14 | name: '{{ namespace }}' 15 | 16 | - import_tasks: kustomize.yml 17 | vars: 18 | state: present 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-closed.yaml: -------------------------------------------------------------------------------- 1 | name: PR Closed 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - main 7 | types: 8 | - closed 9 | 10 | jobs: 11 | cherry_pick_job: 12 | permissions: 13 | pull-requests: write 14 | contents: write 15 | if: github.event.pull_request.merged == true 16 | secrets: inherit 17 | uses: konveyor/release-tools/.github/workflows/cherry-pick.yml@main 18 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-rhsso-keycloak.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: {{ rhsso_api_version }} 3 | kind: Keycloak 4 | metadata: 5 | name: {{ rhsso_service_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app: {{ rhsso_service_name }} 9 | spec: 10 | instances: {{ rhsso_instances | default('1') }} 11 | externalDatabase: 12 | enabled: true 13 | externalAccess: 14 | enabled: {{ rhsso_external_access }} 15 | -------------------------------------------------------------------------------- /helm/templates/rbac/hub_role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: tackle-hub-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | - tackle.konveyor.io 10 | - batch 11 | resources: 12 | - '*' 13 | verbs: 14 | - '*' 15 | - apiGroups: 16 | - security.openshift.io 17 | resourceNames: 18 | - anyuid 19 | resources: 20 | - securitycontextconstraints 21 | verbs: 22 | - use 23 | -------------------------------------------------------------------------------- /roles/tackle/templates/secret-cookie-secret.yml.j2: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 6 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 7 | app.kubernetes.io/part-of: {{ app_name }} 8 | name: cookie-secret 9 | namespace: {{ app_namespace }} 10 | type: Opaque 11 | data: 12 | cookie-secret: {{ new_cookie_secret | b64encode }} 13 | -------------------------------------------------------------------------------- /roles/tackle/templates/secret-hub.yml.j2: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ hub_service_name }} 6 | app.kubernetes.io/component: {{ hub_component_name }} 7 | app.kubernetes.io/part-of: {{ app_name }} 8 | name: {{ hub_secret_name }} 9 | namespace: {{ app_namespace }} 10 | type: Opaque 11 | data: 12 | passphrase: {{ hub_aes_passphrase_b64 }} 13 | addon_token: {{ hub_addon_token_b64 }} 14 | -------------------------------------------------------------------------------- /helm/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/llm-proxy-service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: llm-proxy 6 | namespace: "{{ app_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: llm-proxy 9 | app.kubernetes.io/component: kai 10 | app.kubernetes.io/part-of: tackle 11 | spec: 12 | type: ClusterIP 13 | selector: 14 | app: llm-proxy 15 | ports: 16 | - name: http 17 | port: 8321 18 | targetPort: 8321 19 | protocol: TCP -------------------------------------------------------------------------------- /roles/tackle/templates/secret-keycloak-sso.yml.j2: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 6 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 7 | app.kubernetes.io/part-of: {{ app_name }} 8 | name: {{ keycloak_sso_secret_name }} 9 | namespace: {{ app_namespace }} 10 | type: Opaque 11 | data: 12 | username: {{ keycloak_sso_admin_username_b64 }} 13 | password: {{ keycloak_sso_admin_password_b64 }} 14 | -------------------------------------------------------------------------------- /molecule/kind/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - name: Destroy test kind cluster 11 | command: kind delete cluster --name osdk-test --kubeconfig {{ kubeconfig }} 12 | 13 | - name: Unset pull policy 14 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 15 | args: 16 | chdir: '{{ config_dir }}/testing' 17 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/llm-proxy-client-configmap.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ kai_llm_proxy_client_config_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: llm-proxy-client 9 | app.kubernetes.io/component: kai 10 | app.kubernetes.io/part-of: tackle 11 | konveyor.io/configuration: llm-proxy-client 12 | data: 13 | config.json: | 14 | { 15 | "model": "{{ kai_llm_proxy_provider_id }}/{{ kai_llm_model }}" 16 | } -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_addons.sample.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: tackle.konveyor.io/v1alpha1 3 | kind: Addon 4 | metadata: 5 | name: myaddon 6 | namespace: konveyor-tackle 7 | spec: 8 | container: 9 | image: quay.io/konveyor/tackle2-addon-analyzer:latest 10 | imagePullPolicy: Always 11 | name: myaddon 12 | resources: 13 | limits: 14 | cpu: 1 15 | memory: 1Gi 16 | requests: 17 | cpu: 1 18 | memory: 512Mi 19 | task: mytask 20 | {{ end }} 21 | -------------------------------------------------------------------------------- /tackle-k8s-dev.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: operators.coreos.com/v1 3 | kind: OperatorGroup 4 | metadata: 5 | name: konveyor 6 | namespace: konveyor-tackle 7 | spec: 8 | targetNamespaces: 9 | - konveyor-tackle 10 | --- 11 | apiVersion: operators.coreos.com/v1alpha1 12 | kind: Subscription 13 | metadata: 14 | name: konveyor-operator 15 | namespace: konveyor-tackle 16 | spec: 17 | channel: development 18 | installPlanApproval: Automatic 19 | name: konveyor-operator 20 | source: konveyor 21 | sourceNamespace: konveyor-tackle 22 | -------------------------------------------------------------------------------- /molecule/default/tasks/tackle_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create the tackle.konveyor.io/v1alpha1.Tackle 3 | k8s: 4 | state: present 5 | namespace: '{{ namespace }}' 6 | definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" 7 | wait: yes 8 | wait_timeout: 300 9 | wait_condition: 10 | type: Successful 11 | status: "True" 12 | vars: 13 | cr_file: 'tackle_v1alpha1_tackle.yaml' 14 | 15 | - name: Add assertions here 16 | assert: 17 | that: false 18 | fail_msg: FIXME Add real assertions for your operator 19 | -------------------------------------------------------------------------------- /roles/tackle/templates/secret-keycloak-postgresql.yml.j2: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ keycloak_database_service_name }} 6 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 7 | app.kubernetes.io/part-of: {{ app_name }} 8 | name: {{ keycloak_database_secret_name }} 9 | namespace: {{ app_namespace }} 10 | type: Opaque 11 | data: 12 | database-name: {{ keycloak_database_db_name_b64 }} 13 | database-user: {{ keycloak_database_db_username_b64 }} 14 | database-password: {{ keycloak_database_db_password_b64 }} 15 | -------------------------------------------------------------------------------- /molecule/default/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - import_tasks: kustomize.yml 11 | vars: 12 | state: absent 13 | 14 | - name: Destroy Namespace 15 | k8s: 16 | api_version: v1 17 | kind: Namespace 18 | name: '{{ namespace }}' 19 | state: absent 20 | 21 | - name: Unset pull policy 22 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 23 | args: 24 | chdir: '{{ config_dir }}/testing' 25 | -------------------------------------------------------------------------------- /roles/tackle/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Konveyor Tackle Operator 4 | description: Konveyor Tackle Operator installs a suite of migration tools that facilitate the migration of applications to Kubernetes. 5 | license: Apache 6 | 7 | min_ansible_version: "2.9" 8 | 9 | platforms: 10 | - name: EL 11 | versions: 12 | - '8' 13 | - '9' 14 | 15 | galaxy_tags: 16 | - tackle 17 | - konveyor 18 | - kubernetes 19 | - operator 20 | - application 21 | - migration 22 | - modernization 23 | 24 | dependencies: [] 25 | collections: 26 | - operator_sdk.util 27 | - kubernetes.core 28 | -------------------------------------------------------------------------------- /roles/tackle/templates/servicemonitor-hub.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ hub_service_name }} 6 | app.kubernetes.io/component: {{ hub_component_name }} 7 | app.kubernetes.io/part-of: {{ app_name }} 8 | name: {{ hub_service_name }} 9 | namespace: {{ app_namespace }} 10 | spec: 11 | endpoints: 12 | - interval: 30s 13 | port: metrics 14 | selector: 15 | matchLabels: 16 | app.kubernetes.io/name: {{ hub_service_name }} 17 | app.kubernetes.io/component: {{ hub_component_name }} 18 | app.kubernetes.io/part-of: {{ app_name }} 19 | -------------------------------------------------------------------------------- /molecule/kind/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | 7 | tasks: 8 | - name: Build operator image 9 | docker_image: 10 | build: 11 | path: '{{ project_dir }}' 12 | pull: no 13 | name: '{{ operator_image }}' 14 | tag: latest 15 | push: no 16 | source: build 17 | force_source: yes 18 | 19 | - name: Load image into kind cluster 20 | command: kind load docker-image --name osdk-test '{{ operator_image }}' 21 | register: result 22 | changed_when: '"not yet present" in result.stdout' 23 | 24 | - import_playbook: ../default/converge.yml 25 | -------------------------------------------------------------------------------- /.github/workflows/csv-validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate Bundle CSV 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - 'release-*.*' 8 | paths: 9 | - 'bundle/**' 10 | - 'helm/**' 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | steps: 19 | - name: Checkout PR branch 20 | uses: actions/checkout@v2 21 | with: 22 | ref: ${{ github.event.pull_request.head.sha }} 23 | 24 | - name: Get Operator SDK 25 | run: make operator-sdk 26 | 27 | - name: Check config/ is in sync with bundle/ 28 | run: | 29 | make bundle bundle-sync-check 30 | -------------------------------------------------------------------------------- /roles/tackle/templates/persistentvolumeclaim-cache.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ cache_data_volume_claim_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ cache_name }} 9 | app.kubernetes.io/part-of: {{ app_name }} 10 | volume: {{ cache_data_volume_name }} 11 | non-root: "true" 12 | spec: 13 | accessModes: 14 | - "{{ cache_data_volume_claim_mode }}" 15 | resources: 16 | requests: 17 | storage: {{ cache_data_volume_size }} 18 | {% if cache_storage_class is defined %} 19 | {% if cache_storage_class|length %} 20 | storageClassName: {{ cache_storage_class }} 21 | {% endif %} 22 | {% endif %} 23 | -------------------------------------------------------------------------------- /roles/tackle/templates/secret-keycloak-db.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | POSTGRES_DATABASE: {{ rhsso_db_name_b64 }} 4 | POSTGRES_EXTERNAL_ADDRESS: {{ rhsso_db_host_b64 }} 5 | POSTGRES_HOST: 6 | POSTGRES_PASSWORD: {{ rhsso_db_pass_b64 }} 7 | POSTGRES_USERNAME: {{ rhsso_db_user_b64 }} 8 | POSTGRES_SUPERUSER: {{ "true" | b64encode }} 9 | SSLMODE: {{ "prefer" | b64encode }} 10 | kind: Secret 11 | metadata: 12 | labels: 13 | app: keycloak 14 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 15 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 16 | app.kubernetes.io/part-of: {{ app_name }} 17 | name: keycloak-db-secret 18 | namespace: {{ app_namespace }} 19 | type: Opaque 20 | -------------------------------------------------------------------------------- /roles/tackle/templates/persistentvolumeclaim-hub-database.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ hub_database_volume_claim_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ hub_service_name }} 9 | app.kubernetes.io/component: {{ hub_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | volume: {{ hub_database_volume_name }} 12 | spec: 13 | accessModes: 14 | - ReadWriteOnce 15 | resources: 16 | requests: 17 | storage: {{ hub_database_volume_size }} 18 | {% if rwo_storage_class is defined %} 19 | {% if rwo_storage_class|length %} 20 | storageClassName: {{ rwo_storage_class }} 21 | {% endif %} 22 | {% endif %} 23 | -------------------------------------------------------------------------------- /molecule/default/kustomize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Build kustomize testing overlay 3 | # load_restrictor must be set to none so we can load patch files from the default overlay 4 | command: '{{ kustomize }} build --load-restrictor LoadRestrictionsNone' 5 | args: 6 | chdir: '{{ config_dir }}/testing' 7 | register: resources 8 | changed_when: false 9 | 10 | - name: Set resources to {{ state }} 11 | k8s: 12 | definition: '{{ item }}' 13 | state: '{{ state }}' 14 | wait: no 15 | loop: '{{ resources.stdout | from_yaml_all | list }}' 16 | 17 | - name: Wait for resources to get to {{ state }} 18 | k8s: 19 | definition: '{{ item }}' 20 | state: '{{ state }}' 21 | wait: yes 22 | loop: '{{ resources.stdout | from_yaml_all | list }}' 23 | -------------------------------------------------------------------------------- /roles/tackle/templates/persistentvolumeclaim-hub-bucket.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ hub_bucket_volume_claim_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ hub_service_name }} 9 | app.kubernetes.io/component: {{ hub_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | volume: {{ hub_bucket_volume_name }} 12 | spec: 13 | accessModes: 14 | - {{ hub_bucket_volume_claim_mode }} 15 | resources: 16 | requests: 17 | storage: {{ hub_bucket_volume_size }} 18 | {% if hub_bucket_storage_class is defined %} 19 | {% if hub_bucket_storage_class|length %} 20 | storageClassName: {{ hub_bucket_storage_class }} 21 | {% endif %} 22 | {% endif %} 23 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_extensions.sample.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: tackle.konveyor.io/v1alpha1 3 | kind: Extension 4 | metadata: 5 | name: myextension 6 | namespace: konveyor-tackle 7 | spec: 8 | addon: myaddon 9 | container: 10 | args: 11 | - --port 12 | - $(PORT) 13 | env: 14 | - name: PORT 15 | value: ${seq:8000} 16 | image: quay.io/konveyor/generic-external-provider:latest 17 | imagePullPolicy: Always 18 | name: myextension 19 | resources: 20 | limits: 21 | cpu: 1 22 | memory: 1Gi 23 | requests: 24 | cpu: 1 25 | memory: 1Gi 26 | metadata: 27 | provider: 28 | address: localhost:$(PORT) 29 | name: myextension 30 | selector: tag:Language=MyLang 31 | {{ end }} 32 | -------------------------------------------------------------------------------- /roles/tackle/templates/route-ui.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: route.openshift.io/v1 3 | kind: Route 4 | metadata: 5 | name: {{ ui_route_name }} 6 | namespace: {{ app_namespace }} 7 | annotations: 8 | haproxy.router.openshift.io/timeout: 300s 9 | labels: 10 | app.kubernetes.io/name: {{ ui_service_name }} 11 | app.kubernetes.io/component: route 12 | app.kubernetes.io/part-of: {{ app_name }} 13 | app: {{ app_name }} 14 | spec: 15 | to: 16 | kind: Service 17 | name: {{ ui_service_name }} 18 | tls: 19 | {% if feature_auth_required|bool and feature_auth_type == "oauth" %} 20 | termination: reencrypt 21 | {% else %} 22 | termination: {{ ui_route_tls_termination }} 23 | {% endif %} 24 | insecureEdgeTerminationPolicy: {{ ui_route_tls_insecure_termination_policy }} 25 | -------------------------------------------------------------------------------- /roles/tackle/templates/persistentvolumeclaim-keycloak-postgresql.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ keycloak_database_data_volume_claim_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ keycloak_database_service_name }} 9 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | volume: {{ keycloak_database_data_volume_name }} 12 | spec: 13 | accessModes: 14 | - ReadWriteOnce 15 | resources: 16 | requests: 17 | storage: {{ keycloak_database_data_volume_size }} 18 | {% if rwo_storage_class is defined %} 19 | {% if rwo_storage_class|length %} 20 | storageClassName: {{ rwo_storage_class }} 21 | {% endif %} 22 | {% endif %} 23 | -------------------------------------------------------------------------------- /tools/upgrades/jwt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Usage: jwt.sh 4 | # 5 | # scope - (string) space-separated scopes. (default: *:*). 6 | # 7 | key=$1 8 | scope="${2:-*:*}" 9 | header='{"typ":"JWT","alg":"HS512"}' 10 | payload="{\"user\":\"operator\",\"scope\":\"${scope}\"}" 11 | headerStr=$(echo -n ${header} \ 12 | | base64 -w 0 \ 13 | | sed s/\+/-/g \ 14 | | sed 's/\//_/g' \ 15 | | sed -E s/=+$//) 16 | payloadStr=$(echo -n ${payload} \ 17 | | base64 -w 0 \ 18 | | sed s/\+/-/g \ 19 | | sed 's/\//_/g' \ 20 | | sed -E s/=+$//) 21 | signStr=$(echo -n "${headerStr}.${payloadStr}" \ 22 | | openssl dgst -sha512 -hmac ${key} -binary \ 23 | | base64 -w 0 \ 24 | | sed s/\+/-/g \ 25 | | sed 's/\//_/g' \ 26 | | sed -E s/=+$//) 27 | token="${headerStr}.${payloadStr}.${signStr}" 28 | echo "${token}" 29 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-keycloak-postgresql.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: {{ keycloak_database_service_name }} 7 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 8 | app.kubernetes.io/part-of: {{ app_name }} 9 | name: {{ keycloak_database_service_k8s_resource_name }} 10 | namespace: {{ app_namespace }} 11 | spec: 12 | ports: 13 | - name: postgres 14 | port: 5432 15 | targetPort: 5432 16 | protocol: TCP 17 | selector: 18 | app.kubernetes.io/name: {{ keycloak_database_service_name }}-{{ keycloak_database_db_version }} 19 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 20 | app.kubernetes.io/part-of: {{ app_name }} 21 | version: "{{ keycloak_database_db_version }}" 22 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | platforms: 7 | - name: cluster 8 | groups: 9 | - k8s 10 | provisioner: 11 | name: ansible 12 | inventory: 13 | group_vars: 14 | all: 15 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 16 | host_vars: 17 | localhost: 18 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 19 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 20 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 21 | operator_image: ${OPERATOR_IMAGE:-""} 22 | operator_pull_policy: ${OPERATOR_PULL_POLICY:-"Always"} 23 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 24 | env: 25 | K8S_AUTH_KUBECONFIG: ${KUBECONFIG:-"~/.kube/config"} 26 | verifier: 27 | name: ansible 28 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-keycloak-postgresql-migration.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: {{ keycloak_database_service_name }} 7 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 8 | app.kubernetes.io/part-of: {{ app_name }} 9 | name: {{ keycloak_database_service_k8s_resource_name }}-migration 10 | namespace: {{ app_namespace }} 11 | spec: 12 | ports: 13 | - name: postgres 14 | port: 5432 15 | targetPort: 5432 16 | protocol: TCP 17 | selector: 18 | app.kubernetes.io/name: {{ keycloak_database_service_name }}-{{ keycloak_database_db_version }} 19 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 20 | app.kubernetes.io/part-of: {{ app_name }} 21 | version: "{{ keycloak_database_db_version }}" 22 | -------------------------------------------------------------------------------- /.github/workflows/issues.yaml: -------------------------------------------------------------------------------- 1 | name: Reconcile GitHub Issue (Comment) 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - closed 9 | - reopened 10 | - labeled 11 | - unlabeled 12 | issue_comment: 13 | types: 14 | - created 15 | - edited 16 | 17 | concurrency: 18 | group: reconcile-issue-${{ github.event.issue.number }}-${{ github.event_name }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | reconcile-issue: 23 | if: github.event_name == 'issues' || github.event_name == 'pull_request' 24 | secrets: inherit 25 | uses: konveyor/release-tools/.github/workflows/reconcile-issue.yaml@main 26 | 27 | reconcile-issue-comment: 28 | if: github.event_name == 'issue_comment' 29 | secrets: inherit 30 | uses: konveyor/release-tools/.github/workflows/reconcile-issue-comment.yaml@main 31 | -------------------------------------------------------------------------------- /helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: Konveyor 3 | description: A Helm chart for Konveyor 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: v99.0.0 19 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: konveyor-operator 7 | operators.operatorframework.io.bundle.channels.v1: development 8 | operators.operatorframework.io.bundle.channel.default.v1: development 9 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.35.0 10 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 11 | operators.operatorframework.io.metrics.project_layout: ansible.sdk.operatorframework.io/v1 12 | 13 | # Annotations for testing. 14 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 15 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 16 | -------------------------------------------------------------------------------- /helm/templates/rbac/cluster_role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - operator.openshift.io 9 | resources: 10 | - dnses 11 | verbs: 12 | - list 13 | - get 14 | - apiGroups: 15 | - config.openshift.io 16 | resources: 17 | - clusterversions 18 | - proxies 19 | verbs: 20 | - get 21 | - apiGroups: 22 | - keycloak.org 23 | - k8s.keycloak.org 24 | resources: 25 | - keycloaks 26 | - keycloakrealms 27 | - keycloakclients 28 | - keycloakusers 29 | verbs: 30 | - get 31 | - list 32 | - watch 33 | - create 34 | - update 35 | - patch 36 | - delete 37 | - apiGroups: 38 | - operators.coreos.com 39 | resources: 40 | - subscriptions 41 | - clusterserviceversions 42 | verbs: 43 | - get 44 | - list 45 | - watch 46 | - delete 47 | #+kubebuilder:scaffold:rules 48 | -------------------------------------------------------------------------------- /tackle-k8s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: konveyor-tackle 6 | --- 7 | apiVersion: operators.coreos.com/v1alpha1 8 | kind: CatalogSource 9 | metadata: 10 | name: konveyor 11 | namespace: konveyor-tackle 12 | spec: 13 | displayName: Konveyor Operator 14 | publisher: Konveyor 15 | sourceType: grpc 16 | image: quay.io/konveyor/tackle2-operator-index:latest 17 | --- 18 | apiVersion: operators.coreos.com/v1 19 | kind: OperatorGroup 20 | metadata: 21 | name: konveyor 22 | namespace: konveyor-tackle 23 | spec: 24 | targetNamespaces: 25 | - konveyor-tackle 26 | --- 27 | apiVersion: operators.coreos.com/v1alpha1 28 | kind: Subscription 29 | metadata: 30 | name: konveyor-operator 31 | namespace: konveyor-tackle 32 | spec: 33 | channel: development 34 | installPlanApproval: Automatic 35 | name: konveyor-operator 36 | source: konveyor 37 | sourceNamespace: konveyor-tackle 38 | -------------------------------------------------------------------------------- /molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | 7 | tasks: 8 | - name: Ensure operator image is set 9 | fail: 10 | msg: | 11 | You must specify the OPERATOR_IMAGE environment variable in order to run the 12 | 'default' scenario 13 | when: not operator_image 14 | 15 | - name: Set testing image 16 | command: '{{ kustomize }} edit set image testing={{ operator_image }}' 17 | args: 18 | chdir: '{{ config_dir }}/testing' 19 | 20 | - name: Set pull policy 21 | command: '{{ kustomize }} edit add patch --path pull_policy/{{ operator_pull_policy }}.yaml' 22 | args: 23 | chdir: '{{ config_dir }}/testing' 24 | 25 | - name: Set testing namespace 26 | command: '{{ kustomize }} edit set namespace {{ namespace }}' 27 | args: 28 | chdir: '{{ config_dir }}/testing' 29 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_schemas.samples.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: tackle.konveyor.io/v1alpha1 3 | kind: Schema 4 | metadata: 5 | name: myschema 6 | namespace: konveyor-tackle 7 | spec: 8 | domain: mydomain 9 | variant: myvariant 10 | subject: mysubject 11 | versions: 12 | - definition: 13 | $schema: https://json-schema.org/draft/2020-12/schema 14 | properties: 15 | names: 16 | description: Application names. Each may be a glob expression. 17 | items: 18 | minLength: 1 19 | type: string 20 | minItems: 0 21 | type: array 22 | spaces: 23 | description: Space names. 24 | items: 25 | minLength: 1 26 | type: string 27 | minItems: 1 28 | type: array 29 | required: 30 | - spaces 31 | title: Coordinates 32 | type: object 33 | {{ end }} 34 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-hub.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | {% if hub_tls_enabled|bool and openshift_cluster|bool %} 6 | annotations: 7 | service.beta.openshift.io/serving-cert-secret-name: {{ hub_tls_secret_name }} 8 | {% endif %} 9 | labels: 10 | app.kubernetes.io/name: {{ hub_service_name }} 11 | app.kubernetes.io/component: {{ hub_component_name }} 12 | app.kubernetes.io/part-of: {{ app_name }} 13 | name: {{ hub_service_name }} 14 | namespace: {{ app_namespace }} 15 | spec: 16 | ports: 17 | - name: api 18 | port: {{ hub_port }} 19 | targetPort: {{ hub_port }} 20 | protocol: TCP 21 | - name: metrics 22 | port: {{ hub_metrics_port }} 23 | targetPort: {{ hub_metrics_port }} 24 | protocol: TCP 25 | selector: 26 | app.kubernetes.io/name: {{ hub_service_name }} 27 | app.kubernetes.io/component: {{ hub_component_name }} 28 | app.kubernetes.io/part-of: {{ app_name }} 29 | type: ClusterIP 30 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-keycloak-sso.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | {% if keycloak_sso_tls_enabled|bool and openshift_cluster|bool %} 6 | annotations: 7 | service.beta.openshift.io/serving-cert-secret-name: {{ keycloak_sso_tls_secret_name }} 8 | {% endif %} 9 | labels: 10 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 11 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 12 | app.kubernetes.io/part-of: {{ app_name }} 13 | name: {{ keycloak_sso_service_name }} 14 | namespace: {{ app_namespace }} 15 | spec: 16 | ports: 17 | - name: http 18 | port: 8080 19 | targetPort: 8080 20 | protocol: TCP 21 | - name: https 22 | port: 8443 23 | targetPort: 8443 24 | protocol: TCP 25 | selector: 26 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 27 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 28 | app.kubernetes.io/part-of: {{ app_name }} 29 | type: ClusterIP 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG OPERATOR_SDK_VERSION=v1.37.1 2 | FROM quay.io/operator-framework/ansible-operator:$OPERATOR_SDK_VERSION 3 | 4 | USER 0 5 | COPY tools/upgrades/migrate-pathfinder-assessments.py /usr/local/bin/migrate-pathfinder-assessments.py 6 | COPY tools/upgrades/jwt.sh /usr/local/bin/jwt.sh 7 | RUN dnf -y install openssl && dnf clean all 8 | RUN echo -e "[almalinux9-appstream]" \ 9 | "\nname = almalinux9-appstream" \ 10 | "\nbaseurl = https://repo.almalinux.org/almalinux/9/AppStream/\$basearch/os/" \ 11 | "\nenabled = 1" \ 12 | "\ngpgcheck = 0" > /etc/yum.repos.d/almalinux.repo 13 | RUN dnf -y module enable postgresql:15 && dnf -y install postgresql python3.12-psycopg2 && dnf clean all 14 | RUN pip install jmespath 15 | USER 1001 16 | 17 | COPY requirements.yml ${HOME}/requirements.yml 18 | RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ 19 | && chmod -R ug+rwx ${HOME}/.ansible 20 | 21 | COPY watches.yaml ${HOME}/watches.yaml 22 | COPY roles/ ${HOME}/roles/ 23 | COPY playbooks/ ${HOME}/playbooks/ 24 | -------------------------------------------------------------------------------- /molecule/kind/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | platforms: 7 | - name: cluster 8 | groups: 9 | - k8s 10 | provisioner: 11 | name: ansible 12 | playbooks: 13 | prepare: ../default/prepare.yml 14 | verify: ../default/verify.yml 15 | inventory: 16 | group_vars: 17 | all: 18 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 19 | host_vars: 20 | localhost: 21 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 22 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 23 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 24 | project_dir: ${MOLECULE_PROJECT_DIRECTORY} 25 | operator_image: testing-operator 26 | operator_pull_policy: "Never" 27 | kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" 28 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 29 | env: 30 | K8S_AUTH_KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 31 | KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 32 | verifier: 33 | name: ansible 34 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=konveyor-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=development 9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=development 10 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.39.1 11 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 12 | LABEL operators.operatorframework.io.metrics.project_layout=ansible.sdk.operatorframework.io/v1 13 | 14 | # Labels for testing. 15 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 16 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 17 | 18 | # Copy files to locations specified by labels. 19 | COPY bundle/manifests /manifests/ 20 | COPY bundle/metadata /metadata/ 21 | COPY bundle/tests/scorecard /tests/scorecard/ 22 | -------------------------------------------------------------------------------- /roles/tackle/templates/service-ui.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | {% if (ui_tls_enabled|bool and openshift_cluster|bool) or (feature_auth_required|bool and feature_auth_type == "oauth") %} 6 | annotations: 7 | service.beta.openshift.io/serving-cert-secret-name: {{ ui_tls_secret_name }} 8 | {% endif %} 9 | labels: 10 | app.kubernetes.io/name: {{ ui_service_name }} 11 | app.kubernetes.io/component: {{ ui_component_name }} 12 | app.kubernetes.io/part-of: {{ app_name }} 13 | name: {{ ui_service_name }} 14 | namespace: {{ app_namespace }} 15 | spec: 16 | ports: 17 | {% if feature_auth_required|bool and feature_auth_type == "oauth" %} 18 | - name: ui 19 | port: {{ oauth_ssl_port }} 20 | targetPort: {{ oauth_ssl_port }} 21 | protocol: TCP 22 | {% else %} 23 | - name: ui 24 | port: {{ ui_port }} 25 | targetPort: {{ ui_port }} 26 | protocol: TCP 27 | {% endif %} 28 | selector: 29 | app.kubernetes.io/name: {{ ui_service_name }} 30 | app.kubernetes.io/component: {{ ui_component_name }} 31 | app.kubernetes.io/part-of: {{ app_name }} 32 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-rhbk-keycloak.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: {{ rhbk_api_version }} 2 | kind: Keycloak 3 | metadata: 4 | name: {{ app_name }}-{{ rhbk_name }} 5 | namespace: {{ app_namespace }} 6 | spec: 7 | instances: 1 8 | ingress: 9 | enabled: false 10 | db: 11 | vendor: postgres 12 | database: {{ keycloak_database_db_name }} 13 | host: {{ keycloak_database_service_k8s_resource_name }} 14 | usernameSecret: 15 | name: keycloak-db-secret 16 | key: POSTGRES_USERNAME 17 | passwordSecret: 18 | name: keycloak-db-secret 19 | key: POSTGRES_PASSWORD 20 | proxy: 21 | headers: xforwarded 22 | resources: 23 | limits: 24 | cpu: {{ keycloak_sso_container_limits_cpu }} 25 | memory: {{ keycloak_sso_container_limits_memory }} 26 | requests: 27 | cpu: {{ keycloak_sso_container_requests_cpu }} 28 | memory: {{ keycloak_sso_container_requests_memory }} 29 | http: 30 | tlsSecret: {{ rhbk_tls_secret_name }} 31 | hostname: 32 | strict: false 33 | additionalOptions: 34 | - name: http-relative-path 35 | value: /auth 36 | bootstrapAdmin: 37 | user: 38 | secret: {{ keycloak_sso_secret_name }} 39 | 40 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/kai-api-deployment.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: kai-api 6 | namespace: "{{ app_namespace }}" 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: kai-api 12 | template: 13 | metadata: 14 | labels: 15 | app: kai-api 16 | spec: 17 | containers: 18 | - name: kai-solution-server 19 | image: "{{ kai_fqin }}" 20 | ports: 21 | - containerPort: 8000 22 | env: 23 | - name: MOUNT_PATH 24 | value: /api 25 | - name: KAI_LLM_PARAMS 26 | value: '{{ kai_llm_params | to_json }}' 27 | - name: KAI_DB_DSN 28 | value: 'postgresql+asyncpg://kai:{{ pg_password }}@{{ kai_database_address }}:5432/kai' 29 | {% if kai_api_key_secret_status.resources | length > 0 %} 30 | {% for (key, value) in kai_api_key_secret_status.resources.0.data.items() %} 31 | - name: {{ key }} 32 | valueFrom: 33 | secretKeyRef: 34 | name: "{{ kai_api_key_secret_name }}" 35 | key: {{ key }} 36 | {% endfor %} 37 | {% endif %} 38 | -------------------------------------------------------------------------------- /helm/values.yaml: -------------------------------------------------------------------------------- 1 | version: "99.0.0" 2 | 3 | # true in the case we want to render manifests for OLM stuff 4 | olm: false 5 | csv: 6 | replaces: "" 7 | # replaces: konveyor-operator.v0.3.0-beta.1.1 8 | skips: [] 9 | # skips: 10 | # - konveyor-operator.v0.3.0-beta.1 11 | # - konveyor-operator.v0.3.0-beta.1.1 12 | 13 | images: 14 | operator: quay.io/konveyor/tackle2-operator:latest 15 | oauth_proxy: quay.io/openshift/origin-oauth-proxy:latest 16 | tackle_hub: quay.io/konveyor/tackle2-hub:latest 17 | tackle_postgres: quay.io/sclorg/postgresql-15-c9s:latest 18 | keycloak_sso: quay.io/keycloak/keycloak:26.1 19 | keycloak_init: quay.io/konveyor/tackle-keycloak-init:latest 20 | tackle_ui: quay.io/konveyor/tackle2-ui:latest 21 | addon_analyzer: quay.io/konveyor/tackle2-addon-analyzer:latest 22 | addon_platform: quay.io/konveyor/tackle2-addon-platform:latest 23 | addon_discovery: quay.io/konveyor/tackle2-addon-discovery:latest 24 | provider_generic: quay.io/konveyor/generic-external-provider:latest 25 | provider_java: quay.io/konveyor/java-external-provider:latest 26 | provider_c_sharp: quay.io/konveyor/c-sharp-provider:latest 27 | kantra: quay.io/konveyor/kantra:latest 28 | kai: quay.io/konveyor/kai-solution-server:latest 29 | -------------------------------------------------------------------------------- /roles/tackle/templates/ingress-ui.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: Ingress 4 | metadata: 5 | annotations: 6 | {% if ui_ingress_class_name == 'nginx' %} 7 | nginx.ingress.kubernetes.io/proxy-body-size: {{ ui_ingress_proxy_body_size }} 8 | {% elif ui_ingress_class_name == 'alb' %} 9 | alb.ingress.kubernetes.io/target-type: ip 10 | alb.ingress.kubernetes.io/scheme: internet-facing 11 | {% endif %} 12 | name: {{ ui_ingress_name }} 13 | namespace: {{ app_namespace }} 14 | labels: 15 | app.kubernetes.io/name: {{ ui_ingress_name }} 16 | app.kubernetes.io/component: ingress 17 | app.kubernetes.io/part-of: {{ app_name }} 18 | app: {{ app_name }} 19 | spec: 20 | ingressClassName: {{ ui_ingress_class_name }} 21 | 22 | tls: 23 | - {} 24 | rules: 25 | - http: 26 | paths: 27 | - path: / 28 | {% if ui_ingress_path_type is defined %} 29 | pathType: {{ ui_ingress_path_type }} 30 | {% elif ui_ingress_class_name == 'alb' %} 31 | pathType: Prefix 32 | {% else %} 33 | pathType: ImplementationSpecific 34 | {% endif %} 35 | backend: 36 | service: 37 | name: {{ ui_service_name }} 38 | port: 39 | number: {{ ui_port }} 40 | -------------------------------------------------------------------------------- /hack/start-minikube.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | 5 | # Inputs via environment variables 6 | MINIKUBE_DRIVER="${MINIKUBE_DRIVER:-}" 7 | MINIKUBE_CONTAINER_RUNTIME="${MINIKUBE_CONTAINER_RUNTIME:-}" 8 | MINIKUBE_KUBERNETES_VERSION="${MINIKUBE_KUBERNETES_VERSION:-}" 9 | MINIKUBE_CPUS="${MINIKUBE_CPUS:-}" 10 | MINIKUBE_MEMORY="${MINIKUBE_MEMORY:-}" 11 | MINIKUBE_CNI="${MINIKUBE_CNI:-}" 12 | MINIKUBE_NETWORK="${MINIKUBE_NETWORK:-}" 13 | 14 | # Check pre-reqs 15 | # May want to leave this for the user to install 16 | if ! command -v minikube >/dev/null 2>&1; then 17 | echo "Please install minikube" 18 | exit 1 19 | fi 20 | 21 | # Start minikube if not already started 22 | if ! minikube status; then 23 | ARGS="" 24 | [ -z "${MINIKUBE_DRIVER}" ] || \ 25 | ARGS+=" --driver=${MINIKUBE_DRIVER}" 26 | [ -z "${MINIKUBE_CONTAINER_RUNTIME}" ] || \ 27 | ARGS+=" --container-runtime=${MINIKUBE_CONTAINER_RUNTIME}" 28 | [ -z "${MINIKUBE_KUBERNETES_VERSION}" ] || \ 29 | ARGS+=" --kubernetes-version=${MINIKUBE_KUBERNETES_VERSION}" 30 | [ -z "${MINIKUBE_CPUS}" ] || \ 31 | ARGS+=" --cpus=${MINIKUBE_CPUS}" 32 | [ -z "${MINIKUBE_MEMORY}" ] || \ 33 | ARGS+=" --memory=${MINIKUBE_MEMORY}" 34 | [ -z "${MINIKUBE_CNI}" ] || \ 35 | ARGS+=" --cni=${MINIKUBE_CNI}" 36 | [ -z "${MINIKUBE_NETWORK}" ] || \ 37 | ARGS+=" --network=${MINIKUBE_NETWORK}" 38 | set -x 39 | minikube start ${ARGS} 40 | fi 41 | 42 | # Enable ingress 43 | minikube addons enable ingress 44 | -------------------------------------------------------------------------------- /docs/releases.md: -------------------------------------------------------------------------------- 1 | # Tackle Upstream Release Instructions 2 | 3 | ## Prerequisites 4 | 5 | - Podman 1.6.4+ 6 | - [Operator SDK v1.3.0+](https://github.com/operator-framework/operator-sdk) 7 | - [Opm](https://github.com/operator-framework/operator-registry) for index image manipulation 8 | - [Quay.io](https://quay.io/organization/konveyor) access to Konveyor Tackle2 repos 9 | 10 | ## Overview 11 | The Konveyor Tackle2 new release procedure consist of a few steps summarized below: 12 | - Create a new release branch on Konveyor Tackle2 Operator repo 13 | - Create and submit PR preparing bundle manifests for the new release branch 14 | - Once merged, bundle images for new release will be automatically built on Quay.io 15 | - Build new index images and push new metadata to Quay.io 16 | 17 | ## Stable 18 | We use semantic versioning convention (semver) for stable releases, release branches should be in the form of v 19 | 20 | 1. Create a new release branch in Tackle2 operator repo, for example `v2.0.0` 21 | 1. Create a PR for the new release branch 22 | 1. Run `tools/cut-release.py --version 2.0.0 --project-path .` 23 | 1. Review changes, commmit, and submit the PR against new release branch for review 24 | 1. Once the release PR is ready and merged, add it to the index image and push to quay.io 25 | 1. `tools/push-release-metadata.py --old-version 1.9.9 --new-version 2.0.0` 26 | 1. Create or refresh existing konveyor-tackle catalog source and validate `oc create -f konveyor-operator-catalog.yaml` 27 | -------------------------------------------------------------------------------- /roles/tackle/templates/networkpolicy.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: NetworkPolicy 4 | metadata: 5 | name: {{ app_name }}-deny-all 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app: {{ app_name }} 9 | spec: 10 | podSelector: {} 11 | policyTypes: 12 | - Ingress 13 | --- 14 | kind: NetworkPolicy 15 | apiVersion: networking.k8s.io/v1 16 | metadata: 17 | name: {{ app_name }}-namespace 18 | namespace: {{ app_namespace }} 19 | labels: 20 | app: {{ app_name }} 21 | spec: 22 | podSelector: {} 23 | ingress: 24 | - from: 25 | - namespaceSelector: 26 | matchLabels: 27 | kubernetes.io/metadata.name: {{ app_namespace }} 28 | --- 29 | apiVersion: networking.k8s.io/v1 30 | kind: NetworkPolicy 31 | metadata: 32 | name: {{ app_name }}-external 33 | namespace: {{ app_namespace }} 34 | labels: 35 | app: {{ app_name }} 36 | spec: 37 | podSelector: 38 | matchLabels: 39 | role: {{ ui_service_name }} 40 | ingress: 41 | - ports: 42 | - port: 8080 43 | - port: 8443 44 | --- 45 | apiVersion: networking.k8s.io/v1 46 | kind: NetworkPolicy 47 | metadata: 48 | name: {{ app_name }}-metrics 49 | namespace: {{ app_namespace }} 50 | labels: 51 | app: {{ app_name }} 52 | spec: 53 | podSelector: 54 | matchLabels: 55 | role: {{ hub_service_name }} 56 | ingress: 57 | - from: 58 | - namespaceSelector: 59 | matchLabels: 60 | network.openshift.io/policy-group: monitoring 61 | - ports: 62 | - port: {{ hub_metrics_port }} 63 | -------------------------------------------------------------------------------- /helm/templates/olm/scorecard.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: scorecard.operatorframework.io/v1alpha3 3 | kind: Configuration 4 | metadata: 5 | name: config 6 | stages: 7 | - parallel: true 8 | tests: 9 | - entrypoint: 10 | - scorecard-test 11 | - basic-check-spec 12 | image: quay.io/operator-framework/scorecard-test:v1.22 13 | labels: 14 | suite: basic 15 | test: basic-check-spec-test 16 | - entrypoint: 17 | - scorecard-test 18 | - olm-bundle-validation 19 | image: quay.io/operator-framework/scorecard-test:v1.22 20 | labels: 21 | suite: olm 22 | test: olm-bundle-validation-test 23 | - entrypoint: 24 | - scorecard-test 25 | - olm-crds-have-validation 26 | image: quay.io/operator-framework/scorecard-test:v1.22 27 | labels: 28 | suite: olm 29 | test: olm-crds-have-validation-test 30 | - entrypoint: 31 | - scorecard-test 32 | - olm-crds-have-resources 33 | image: quay.io/operator-framework/scorecard-test:v1.22 34 | labels: 35 | suite: olm 36 | test: olm-crds-have-resources-test 37 | - entrypoint: 38 | - scorecard-test 39 | - olm-spec-descriptors 40 | image: quay.io/operator-framework/scorecard-test:v1.22 41 | labels: 42 | suite: olm 43 | test: olm-spec-descriptors-test 44 | - entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.22 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | {{ end}} 52 | -------------------------------------------------------------------------------- /.github/workflows/pr-ci.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - 'release-*.*' 8 | 9 | jobs: 10 | detect-changes: 11 | runs-on: ubuntu-latest 12 | # Required permissions 13 | permissions: 14 | pull-requests: read 15 | # Set job outputs to values from filter step 16 | outputs: 17 | roles: ${{ steps.filter.outputs.roles }} 18 | steps: 19 | # For pull requests it's not necessary to checkout the code 20 | - uses: dorny/paths-filter@v2 21 | id: filter 22 | with: 23 | filters: | 24 | roles: 25 | - 'roles/**' 26 | 27 | ansible-lint: 28 | needs: detect-changes 29 | if: ${{ needs.detect-changes.outputs.roles == 'true' }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout PR branch 33 | uses: actions/checkout@v4 34 | - name: Ansible Lint 35 | uses: ansible/ansible-lint@main 36 | with: 37 | setup_python: "false" 38 | working_directory: "roles/" 39 | 40 | build-operator-bundle: 41 | needs: detect-changes 42 | runs-on: ubuntu-latest 43 | env: 44 | IMG: ttl.sh/konveyor-operator-${{ github.sha }}:2h 45 | BUNDLE_IMG: ttl.sh/konveyor-operator-bundle-${{ github.sha }}:2h 46 | steps: 47 | - uses: actions/checkout@v4 48 | - run: make docker-build docker-push 49 | - run: make bundle bundle-build bundle-push 50 | 51 | 52 | run-ci: 53 | needs: build-operator-bundle 54 | uses: konveyor/ci/.github/workflows/global-ci-bundle.yml@main 55 | with: 56 | operator_bundle: ttl.sh/konveyor-operator-bundle-${{ github.sha }}:2h 57 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-task.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Task 3 | apiVersion: tackle.konveyor.io/v1alpha1 4 | metadata: 5 | namespace: {{ app_namespace }} 6 | name: {{ analyzer_name }} 7 | spec: 8 | priority: 10 9 | dependencies: [ {{ language_discovery_name }} ] 10 | 11 | --- 12 | kind: Task 13 | apiVersion: tackle.konveyor.io/v1alpha1 14 | metadata: 15 | namespace: {{ app_namespace }} 16 | name: {{ tech_discovery_name }} 17 | labels: 18 | konveyor.io/discovery: "technology" 19 | spec: 20 | priority: 1 21 | dependencies: [ {{ language_discovery_name }} ] 22 | data: 23 | mode: 24 | discovery: true 25 | withDeps: true 26 | tagger: 27 | enabled: true 28 | source: tech-discovery 29 | rules: 30 | labels: 31 | included: ["discovery"] 32 | 33 | --- 34 | kind: Task 35 | apiVersion: tackle.konveyor.io/v1alpha1 36 | metadata: 37 | namespace: {{ app_namespace }} 38 | name: {{ language_discovery_name }} 39 | labels: 40 | konveyor.io/discovery: "language" 41 | spec: 42 | data: 43 | source: language-discovery 44 | 45 | --- 46 | kind: Task 47 | apiVersion: tackle.konveyor.io/v1alpha1 48 | metadata: 49 | namespace: {{ app_namespace }} 50 | name: {{ application_import }} 51 | spec: 52 | data: 53 | action: import 54 | 55 | --- 56 | kind: Task 57 | apiVersion: tackle.konveyor.io/v1alpha1 58 | metadata: 59 | namespace: {{ app_namespace }} 60 | name: {{ application_manifest }} 61 | spec: 62 | data: 63 | action: fetch 64 | 65 | --- 66 | kind: Task 67 | apiVersion: tackle.konveyor.io/v1alpha1 68 | metadata: 69 | namespace: {{ app_namespace }} 70 | name: {{ asset_generation }} 71 | spec: 72 | data: 73 | action: generate 74 | 75 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - kubernetes.core 8 | 9 | vars: 10 | ctrl_label: control-plane=controller-manager 11 | 12 | tasks: 13 | - block: 14 | - name: Import all test files from tasks/ 15 | include_tasks: '{{ item }}' 16 | with_fileglob: 17 | - tasks/*_test.yml 18 | rescue: 19 | - name: Retrieve relevant resources 20 | k8s_info: 21 | api_version: '{{ item.api_version }}' 22 | kind: '{{ item.kind }}' 23 | namespace: '{{ namespace }}' 24 | loop: 25 | - api_version: v1 26 | kind: Pod 27 | - api_version: apps/v1 28 | kind: Deployment 29 | - api_version: v1 30 | kind: Secret 31 | - api_version: v1 32 | kind: ConfigMap 33 | register: debug_resources 34 | 35 | - name: Retrieve Pod logs 36 | k8s_log: 37 | name: '{{ item.metadata.name }}' 38 | namespace: '{{ namespace }}' 39 | container: manager 40 | loop: "{{ q('k8s', api_version='v1', kind='Pod', namespace=namespace, label_selector=ctrl_label) }}" 41 | register: debug_logs 42 | 43 | - name: Output gathered resources 44 | debug: 45 | var: debug_resources 46 | 47 | - name: Output gathered logs 48 | debug: 49 | var: item.log_lines 50 | loop: '{{ debug_logs.results }}' 51 | 52 | - name: Re-emit failure 53 | vars: 54 | failed_task: 55 | result: '{{ ansible_failed_result }}' 56 | fail: 57 | msg: '{{ failed_task }}' 58 | -------------------------------------------------------------------------------- /.github/actions/start-minikube/action.yml: -------------------------------------------------------------------------------- 1 | name: Start minikube 2 | description: | 3 | Start minikube and prepare it for Tackle. 4 | inputs: 5 | driver: 6 | description: 'Choose a specific driver, one of: docker, none, podman, virtualbox, parallels, vmwarefusion, hyperkit, vmware, ssh' 7 | required: false 8 | default: '' 9 | container-runtime: 10 | description: 'Choose a specific container-runtime, one of: docker, containerd, cri-o' 11 | required: false 12 | default: '' 13 | kubernetes-version: 14 | description: 'Choose a specific version of Kubernetes, "stable" for the latest stable build, or "latest" for the latest development build' 15 | required: false 16 | default: '' 17 | cpus: 18 | description: 'Number of CPUs allocated to Kubernetes. Use "max" to use the maximum number of CPUs.' 19 | required: false 20 | default: '' 21 | memory: 22 | description: 'Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g). Use "max" to use the maximum amount of memory.' 23 | required: false 24 | default: '' 25 | cni: 26 | description: 'CNI plug-in to use. Valid options: auto, bridge, calico, cilium, flannel, kindnet, or path to a CNI manifest' 27 | required: false 28 | default: 'auto' 29 | runs: 30 | using: "composite" 31 | steps: 32 | - name: Setup minikube 33 | uses: medyagh/setup-minikube@master 34 | with: 35 | driver: ${{ inputs.driver }} 36 | container-runtime: ${{ inputs.container-runtime }} 37 | kubernetes-version: ${{ inputs.kubernetes-version }} 38 | cpus: ${{ inputs.cpus }} 39 | memory: 8000 40 | cni: ${{ inputs.cni }} 41 | # Don't pass minikube start ${ARGS} as they are handled in setup-minikube 42 | - name: Start minikube 43 | run: make start-minikube 44 | working-directory: ${{ github.action_path }}/../../.. 45 | shell: bash 46 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark Stale Issues and PRs 2 | 3 | on: 4 | schedule: 5 | # Runs daily at 1:00 AM UTC 6 | - cron: '0 1 * * *' 7 | workflow_dispatch: # Allows manual trigger 8 | 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | 13 | jobs: 14 | stale: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/stale@v9 18 | with: 19 | # Mark as stale after 60 days of inactivity 20 | days-before-stale: 60 21 | 22 | # Never auto-close (set to -1 to disable) 23 | days-before-close: -1 24 | 25 | # Stale issue configuration 26 | stale-issue-message: | 27 | This issue has been automatically marked as stale because it has not had any activity for 60 days. 28 | It will remain open for visibility and reporting purposes. 29 | Please comment if this issue is still relevant. 30 | stale-issue-label: 'stale' 31 | 32 | # Stale PR configuration 33 | stale-pr-message: | 34 | This pull request has been automatically marked as stale because it has not had any activity for 60 days. 35 | It will remain open for visibility and reporting purposes. 36 | Please comment if this PR is still relevant. 37 | stale-pr-label: 'stale' 38 | 39 | # Remove stale label when issues/PRs receive new comments or updates 40 | remove-stale-when-updated: true 41 | 42 | # Exempt items with these labels from being marked stale 43 | exempt-issue-labels: 'security,critical,pinned,blocked,in-progress,under-review,enhancement,tech-debt' 44 | exempt-pr-labels: 'security,critical,pinned,blocked,in-progress,under-review,enhancement,tech-debt' 45 | 46 | # Exempt items with assignees 47 | exempt-all-issue-assignees: true 48 | exempt-all-pr-assignees: true 49 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.22 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.22 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.22 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.22 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.22 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.22 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_tackles.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.10.0 7 | creationTimestamp: null 8 | name: tackles.tackle.konveyor.io 9 | spec: 10 | group: tackle.konveyor.io 11 | names: 12 | kind: Tackle 13 | listKind: TackleList 14 | plural: tackles 15 | singular: tackle 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | description: Tackle defines a tackle application. 22 | properties: 23 | apiVersion: 24 | description: 'APIVersion defines the versioned schema of this representation 25 | of an object. Servers should convert recognized schemas to the latest 26 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 27 | type: string 28 | kind: 29 | description: 'Kind is a string value representing the REST resource this 30 | object represents. Servers may infer this from the endpoint the client 31 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 32 | type: string 33 | metadata: 34 | type: object 35 | spec: 36 | description: Spec defines the desired state of the resource. 37 | type: object 38 | x-kubernetes-preserve-unknown-fields: true 39 | status: 40 | description: Status defines the observed state of the resource. 41 | type: object 42 | x-kubernetes-preserve-unknown-fields: true 43 | type: object 44 | served: true 45 | storage: true 46 | subresources: 47 | status: {} 48 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/kai-db-deployment.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: kai-db 6 | namespace: {{ app_namespace }} 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: kai-db 12 | template: 13 | metadata: 14 | labels: 15 | app: kai-db 16 | spec: 17 | containers: 18 | - name: kai-db 19 | image: "{{ kai_database_image_fqin }}" 20 | env: 21 | - name: POSTGRESQL_DATABASE 22 | valueFrom: 23 | secretKeyRef: 24 | name: "{{ kai_database_secret_name }}" 25 | key: POSTGRESQL_DATABASE 26 | - name: POSTGRESQL_PASSWORD 27 | valueFrom: 28 | secretKeyRef: 29 | name: "{{ kai_database_secret_name }}" 30 | key: POSTGRESQL_PASSWORD 31 | - name: POSTGRES_PASSWORD 32 | valueFrom: 33 | secretKeyRef: 34 | name: "{{ kai_database_secret_name }}" 35 | key: POSTGRESQL_PASSWORD 36 | - name: POSTGRES_USER 37 | valueFrom: 38 | secretKeyRef: 39 | name: "{{ kai_database_secret_name }}" 40 | key: POSTGRESQL_USER 41 | - name: POSTGRES_DB 42 | valueFrom: 43 | secretKeyRef: 44 | name: "{{ kai_database_secret_name }}" 45 | key: POSTGRESQL_DATABASE 46 | - name: POSTGRESQL_USER 47 | valueFrom: 48 | secretKeyRef: 49 | name: "{{ kai_database_secret_name }}" 50 | key: POSTGRESQL_USER 51 | volumeMounts: 52 | - mountPath: /var/lib/postgresql/data 53 | name: kai-db-data 54 | volumes: 55 | - name: kai-db-data 56 | persistentVolumeClaim: 57 | claimName: "{{ kai_database_volume_claim_name }}" 58 | -------------------------------------------------------------------------------- /bundle/manifests/tackle.konveyor.io_tackles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.10.0 6 | creationTimestamp: null 7 | name: tackles.tackle.konveyor.io 8 | spec: 9 | group: tackle.konveyor.io 10 | names: 11 | kind: Tackle 12 | listKind: TackleList 13 | plural: tackles 14 | singular: tackle 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Tackle defines a tackle application. 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: Spec defines the desired state of the resource. 36 | type: object 37 | x-kubernetes-preserve-unknown-fields: true 38 | status: 39 | description: Status defines the observed state of the resource. 40 | type: object 41 | x-kubernetes-preserve-unknown-fields: true 42 | type: object 43 | served: true 44 | storage: true 45 | subresources: 46 | status: {} 47 | status: 48 | acceptedNames: 49 | kind: "" 50 | plural: "" 51 | conditions: null 52 | storedVersions: null 53 | -------------------------------------------------------------------------------- /.github/actions/install-konveyor/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Konveyor 2 | description: | 3 | Install Konveyor Operator. 4 | inputs: 5 | bundle_image: 6 | description: "image url for operator bundle container image" 7 | required: false 8 | default: "" 9 | namespace: 10 | description: "the namespace where konveyor should be installed" 11 | required: false 12 | default: "" 13 | tackle_cr: 14 | description: "JSON encoded Tackle Custom Resource (CR) string" 15 | required: false 16 | default: "" 17 | disable_maven_search: 18 | description: "Disable maven central search" 19 | required: false 20 | default: "true" 21 | runs: 22 | using: "composite" 23 | steps: 24 | - name: Install kubectl 25 | shell: bash 26 | run: | 27 | if command -v kubectl >/dev/null 2>&1; then 28 | echo "kubectl is already installed...yay" 29 | exit 0 30 | fi 31 | curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" 32 | sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl 33 | - name: Install operator-sdk 34 | shell: bash 35 | run: | 36 | if command -v operator-sdk >/dev/null 2>&1; then 37 | echo "operator-sdk is already installed...yay" 38 | exit 0 39 | fi 40 | curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.35.0/operator-sdk_linux_amd64 41 | sudo install -o root -g root -m 0755 operator-sdk_linux_amd64 /usr/local/bin/operator-sdk 42 | - name: Install Konveyor 43 | env: 44 | OPERATOR_BUNDLE_IMAGE: ${{ inputs.bundle_image }} 45 | NAMESPACE: ${{ inputs.namespace }} 46 | TACKLE_CR: ${{ inputs.tackle_cr }} 47 | DISABLE_MAVEN_SEARCH: ${{ inputs.disable_maven_search }} 48 | run: make install-konveyor 49 | working-directory: ${{ github.action_path }}/../../.. 50 | shell: bash 51 | - name: Upload logs on fail 52 | if: ${{ failure() }} 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: debug-output 56 | path: /tmp/konveyor-debug 57 | -------------------------------------------------------------------------------- /docs/installation-macos.md: -------------------------------------------------------------------------------- 1 | # Konveyor local installation guide for MacOS using Minikube & Podman 2 | 3 | ## Prerequisites: 4 | * [Podman](https://podman.io/getting-started/installation) 5 | * [Minikube](https://minikube.sigs.k8s.io/docs/start/) 6 | 7 | ## Installation: 8 | 1. Initialize `podman machine` using the below config 9 | ``` 10 | podman machine init --cpus 2 --memory 10240 --disk-size 20 11 | podman machine set --rootful 12 | podman machine start 13 | ``` 14 | 2. Create a Minikube cluster with podman as the driver option 15 | `minikube start --memory=9g --driver podman` 16 | 3. Install ingress addon 17 | `minikube addons enable ingress` 18 | 4. Install OLM to manage Konveyor operator 19 | `curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.27.0/install.sh | bash -s v0.27.0` 20 | 5. Install Konveyor operator 21 | `kubectl create -f https://operatorhub.io/install/konveyor-operator.yaml` 22 | 6. Verify if the Konveyor operator pod is running or not 23 | `kubectl get pods -n my-konveyor-operator` 24 | 7. Once the operator pod is running, create a Tackle instance using the following 25 | ``` 26 | cat << EOF | kubectl apply -f - 27 | kind: Tackle 28 | apiVersion: tackle.konveyor.io/v1alpha1 29 | metadata: 30 | name: tackle 31 | namespace: my-konveyor-operator 32 | spec: 33 | feature_auth_required: false 34 | EOF 35 | 36 | ``` 37 | 38 | 8. Wait until Tackle pods are in running state 39 | ``` 40 | $ kubectl get pods -n my-konveyor-operator 41 | NAME READY STATUS RESTARTS AGE 42 | tackle-hub-7f7cc9d574-b5kkl 1/1 Running 0 109m 43 | tackle-operator-56c574d689-jmvs7 1/1 Running 0 111m 44 | tackle-ui-5bdb565bcd-g6gsr 1/1 Running 0 109m 45 | task-1-x6fmv 0/1 Completed 0 4m6s 46 | ``` 47 | 48 | 9. Once they are running, access the Tackle UI, 49 | `kubectl port-forward service/tackle-ui 8080:8080 -n my-konveyor-operator` 50 | 51 | *Note: add `--address` param if using aws ec2 instances* 52 | -------------------------------------------------------------------------------- /helm/templates/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: manager-role 6 | rules: 7 | ## 8 | ## Base operator rules 9 | ## 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - pods 14 | - services 15 | - services/finalizers 16 | - endpoints 17 | - persistentvolumeclaims 18 | - events 19 | - configmaps 20 | - secrets 21 | - serviceaccounts 22 | verbs: 23 | - '*' 24 | - apiGroups: 25 | - route.openshift.io 26 | resources: 27 | - routes 28 | verbs: 29 | - '*' 30 | - apiGroups: 31 | - networking.k8s.io 32 | resources: 33 | - ingresses 34 | - networkpolicies 35 | verbs: 36 | - '*' 37 | - apiGroups: 38 | - apps 39 | resources: 40 | - deployments 41 | - daemonsets 42 | - replicasets 43 | - statefulsets 44 | verbs: 45 | - '*' 46 | - apiGroups: 47 | - monitoring.coreos.com 48 | resources: 49 | - servicemonitors 50 | verbs: 51 | - get 52 | - list 53 | - watch 54 | - create 55 | - update 56 | - patch 57 | - delete 58 | - apiGroups: 59 | - apps.openshift.io 60 | resources: 61 | - deploymentconfigs 62 | verbs: 63 | - get 64 | - list 65 | - watch 66 | - create 67 | - update 68 | - patch 69 | - delete 70 | - apiGroups: 71 | - apps 72 | resourceNames: 73 | - tackle-operator 74 | resources: 75 | - deployments/finalizers 76 | verbs: 77 | - update 78 | - apiGroups: 79 | - coordination.k8s.io 80 | resources: 81 | - leases 82 | verbs: 83 | - get 84 | - list 85 | - watch 86 | - create 87 | - update 88 | - patch 89 | - delete 90 | ## 91 | ## Rules for tackle.konveyor.io/v1alpha1, Kind: Tackle 92 | ## 93 | - apiGroups: 94 | - tackle.konveyor.io 95 | resources: 96 | - tackles 97 | - tackles/status 98 | - tackles/finalizers 99 | - addons 100 | - extensions 101 | - tasks 102 | - schemas 103 | verbs: 104 | - create 105 | - delete 106 | - get 107 | - list 108 | - patch 109 | - update 110 | - watch 111 | #+kubebuilder:scaffold:rules 112 | -------------------------------------------------------------------------------- /.github/workflows/march-image-build-push.yml: -------------------------------------------------------------------------------- 1 | name: 'Build and Push Multi-Arch Image' 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - 'main' 8 | - 'release-*' 9 | tags: 10 | - 'v*' 11 | 12 | concurrency: 13 | group: march-build-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | push-quay: 18 | name: Build and Push Manifest 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | steps: 23 | - name: Build Operator Container 24 | uses: konveyor/release-tools/build-push-quay@main 25 | with: 26 | architectures: "amd64, arm64, ppc64le, s390x" 27 | containerfile: "./Dockerfile" 28 | image_name: "tackle2-operator" 29 | image_namespace: "konveyor" 30 | image_registry: "quay.io" 31 | quay_publish_robot: ${{ secrets.QUAY_PUBLISH_ROBOT }} 32 | quay_publish_token: ${{ secrets.QUAY_PUBLISH_TOKEN }} 33 | ref: ${{ github.ref }} 34 | 35 | - name: Build Operator Bundle 36 | uses: konveyor/release-tools/build-push-quay@main 37 | with: 38 | architectures: "amd64" 39 | containerfile: "./bundle.Dockerfile" 40 | image_name: "tackle2-operator-bundle" 41 | image_namespace: "konveyor" 42 | image_registry: "quay.io" 43 | quay_publish_robot: ${{ secrets.QUAY_PUBLISH_ROBOT }} 44 | quay_publish_token: ${{ secrets.QUAY_PUBLISH_TOKEN }} 45 | ref: ${{ github.ref }} 46 | 47 | - name: Build Operator Index 48 | uses: konveyor/release-tools/build-push-quay@main 49 | with: 50 | architectures: "amd64, arm64" 51 | pre_build_cmd: | 52 | CONTAINER_RUNTIME=podman \ 53 | CATALOG_IMG=quay.io/konveyor/tackle2-operator-index:latest \ 54 | BUNDLE_IMG=quay.io/konveyor/tackle2-operator-bundle:latest \ 55 | make catalog-index 56 | containerfile: "./index.Dockerfile" 57 | image_name: "tackle2-operator-index" 58 | image_namespace: "konveyor" 59 | image_registry: "quay.io" 60 | quay_publish_robot: ${{ secrets.QUAY_PUBLISH_ROBOT }} 61 | quay_publish_token: ${{ secrets.QUAY_PUBLISH_TOKEN }} 62 | ref: ${{ github.ref }} 63 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_tasks.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.10.0 7 | creationTimestamp: null 8 | name: tasks.tackle.konveyor.io 9 | spec: 10 | group: tackle.konveyor.io 11 | names: 12 | kind: Task 13 | listKind: TaskList 14 | plural: tasks 15 | singular: task 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | description: Task defines a hub task. 22 | properties: 23 | apiVersion: 24 | description: 'APIVersion defines the versioned schema of this representation 25 | of an object. Servers should convert recognized schemas to the latest 26 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 27 | type: string 28 | kind: 29 | description: 'Kind is a string value representing the REST resource this 30 | object represents. Servers may infer this from the endpoint the client 31 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 32 | type: string 33 | metadata: 34 | type: object 35 | spec: 36 | description: Spec defines the desired state the resource. 37 | properties: 38 | data: 39 | description: Data object passed to the addon. 40 | type: object 41 | x-kubernetes-preserve-unknown-fields: true 42 | dependencies: 43 | description: Dependencies defines a list of task names on which this 44 | task depends. 45 | items: 46 | type: string 47 | type: array 48 | priority: 49 | description: Priority defines the task priority (0-n). 50 | type: integer 51 | type: object 52 | status: 53 | description: Status defines the observed state the resource. 54 | properties: 55 | observedGeneration: 56 | description: The most recent generation observed by the controller. 57 | format: int64 58 | type: integer 59 | type: object 60 | type: object 61 | served: true 62 | storage: true 63 | subresources: 64 | status: {} 65 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-addon.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Addon 3 | apiVersion: tackle.konveyor.io/v1alpha1 4 | metadata: 5 | name: {{ analyzer_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ analyzer_service_name }} 9 | app.kubernetes.io/component: {{ analyzer_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | spec: 12 | task: ^({{ analyzer_name }}|{{ tech_discovery_name }})$ 13 | container: 14 | name: {{ analyzer_component_name }} 15 | image: {{ analyzer_fqin }} 16 | imagePullPolicy: {{ image_pull_policy }} 17 | resources: 18 | limits: 19 | cpu: {{ analyzer_container_limits_cpu }} 20 | memory: {{ analyzer_container_limits_memory }} 21 | requests: 22 | cpu: {{ analyzer_container_requests_cpu }} 23 | memory: {{ analyzer_container_requests_memory }} 24 | 25 | --- 26 | kind: Addon 27 | apiVersion: tackle.konveyor.io/v1alpha1 28 | metadata: 29 | name: {{ language_discovery_name }} 30 | namespace: {{ app_namespace }} 31 | labels: 32 | app.kubernetes.io/name: {{ language_discovery_service_name }} 33 | app.kubernetes.io/component: {{ language_discovery_component_name }} 34 | app.kubernetes.io/part-of: {{ app_name }} 35 | spec: 36 | task: {{ language_discovery_name }} 37 | container: 38 | name: {{ language_discovery_component_name }} 39 | image: {{ language_discovery_fqin }} 40 | imagePullPolicy: {{ image_pull_policy }} 41 | resources: 42 | limits: 43 | cpu: {{ language_discovery_container_limits_cpu }} 44 | memory: {{ language_discovery_container_limits_memory }} 45 | requests: 46 | cpu: {{ language_discovery_container_requests_cpu }} 47 | memory: {{ language_discovery_container_requests_memory }} 48 | 49 | --- 50 | kind: Addon 51 | apiVersion: tackle.konveyor.io/v1alpha1 52 | metadata: 53 | name: {{ platform_name }} 54 | namespace: {{ app_namespace }} 55 | labels: 56 | app.kubernetes.io/name: {{ platform_service_name }} 57 | app.kubernetes.io/component: {{ platform_component_name }} 58 | app.kubernetes.io/part-of: {{ app_name }} 59 | spec: 60 | task: ^({{ application_import }}|{{ application_manifest }}|{{ asset_generation }})$ 61 | container: 62 | name: {{ platform_component_name }} 63 | image: {{ platform_fqin }} 64 | imagePullPolicy: {{ image_pull_policy }} 65 | resources: 66 | limits: 67 | cpu: {{ platform_container_limits_cpu }} 68 | memory: {{ platform_container_limits_memory }} 69 | requests: 70 | cpu: {{ platform_container_requests_cpu }} 71 | memory: {{ platform_container_requests_memory }} 72 | 73 | -------------------------------------------------------------------------------- /bundle/manifests/tackle.konveyor.io_tasks.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.10.0 6 | creationTimestamp: null 7 | name: tasks.tackle.konveyor.io 8 | spec: 9 | group: tackle.konveyor.io 10 | names: 11 | kind: Task 12 | listKind: TaskList 13 | plural: tasks 14 | singular: task 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Task defines a hub task. 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: Spec defines the desired state the resource. 36 | properties: 37 | data: 38 | description: Data object passed to the addon. 39 | type: object 40 | x-kubernetes-preserve-unknown-fields: true 41 | dependencies: 42 | description: Dependencies defines a list of task names on which this 43 | task depends. 44 | items: 45 | type: string 46 | type: array 47 | priority: 48 | description: Priority defines the task priority (0-n). 49 | type: integer 50 | type: object 51 | status: 52 | description: Status defines the observed state the resource. 53 | properties: 54 | observedGeneration: 55 | description: The most recent generation observed by the controller. 56 | format: int64 57 | type: integer 58 | type: object 59 | type: object 60 | served: true 61 | storage: true 62 | subresources: 63 | status: {} 64 | status: 65 | acceptedNames: 66 | kind: "" 67 | plural: "" 68 | conditions: null 69 | storedVersions: null 70 | -------------------------------------------------------------------------------- /helm/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: tackle-operator 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: tackle 10 | name: tackle-operator 11 | template: 12 | metadata: 13 | labels: 14 | app: tackle 15 | name: tackle-operator 16 | role: tackle-operator 17 | spec: 18 | serviceAccountName: tackle-operator 19 | containers: 20 | - args: 21 | - --health-probe-bind-address=:6789 22 | - --metrics-bind-address=127.0.0.1:8080 23 | - --leader-elect 24 | - --leader-election-id=tackle-operator 25 | env: 26 | - name: WATCH_NAMESPACE 27 | valueFrom: 28 | fieldRef: 29 | fieldPath: metadata.namespace 30 | - name: ANSIBLE_GATHERING 31 | value: explicit 32 | - name: APP_NAME 33 | value: tackle 34 | - name: PROFILE 35 | value: konveyor 36 | - name: VERSION 37 | value: {{ .Values.version }} 38 | - name: RELATED_IMAGE_OAUTH_PROXY 39 | value: {{ .Values.images.oauth_proxy }} 40 | - name: RELATED_IMAGE_TACKLE_HUB 41 | value: {{ .Values.images.tackle_hub }} 42 | - name: RELATED_IMAGE_TACKLE_POSTGRES 43 | value: {{ .Values.images.tackle_postgres }} 44 | - name: RELATED_IMAGE_KEYCLOAK_SSO 45 | value: {{ .Values.images.keycloak_sso }} 46 | - name: RELATED_IMAGE_KEYCLOAK_INIT 47 | value: {{ .Values.images.keycloak_init }} 48 | - name: RELATED_IMAGE_TACKLE_UI 49 | value: {{ .Values.images.tackle_ui }} 50 | - name: RELATED_IMAGE_ADDON_ANALYZER 51 | value: {{ .Values.images.addon_analyzer }} 52 | - name: RELATED_IMAGE_ADDON_DISCOVERY 53 | value: {{ .Values.images.addon_discovery }} 54 | - name: RELATED_IMAGE_ADDON_PLATFORM 55 | value: {{ .Values.images.addon_platform }} 56 | - name: RELATED_IMAGE_PROVIDER_GENERIC 57 | value: {{ .Values.images.provider_generic }} 58 | - name: RELATED_IMAGE_PROVIDER_JAVA 59 | value: {{ .Values.images.provider_java }} 60 | - name: RELATED_IMAGE_PROVIDER_C_SHARP 61 | value: {{ .Values.images.provider_c_sharp }} 62 | - name: RELATED_IMAGE_KANTRA 63 | value: {{ .Values.images.kantra }} 64 | - name: RELATED_IMAGE_KAI 65 | value: {{ .Values.images.kai }} 66 | name: tackle-operator 67 | image: {{ .Values.images.operator }} 68 | imagePullPolicy: Always 69 | livenessProbe: 70 | httpGet: 71 | path: /healthz 72 | port: 6789 73 | initialDelaySeconds: 15 74 | periodSeconds: 20 75 | readinessProbe: 76 | httpGet: 77 | path: /readyz 78 | port: 6789 79 | initialDelaySeconds: 5 80 | periodSeconds: 10 81 | resources: 82 | limits: 83 | cpu: "1" 84 | memory: 2Gi 85 | requests: 86 | cpu: 10m 87 | memory: 256Mi 88 | securityContext: 89 | allowPrivilegeEscalation: false 90 | securityContext: 91 | runAsNonRoot: true 92 | -------------------------------------------------------------------------------- /helm/templates/crds/tackle.konveyor.io_schemas.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.10.0 7 | creationTimestamp: null 8 | name: schemas.tackle.konveyor.io 9 | spec: 10 | group: tackle.konveyor.io 11 | names: 12 | kind: Schema 13 | listKind: SchemaList 14 | plural: schemas 15 | singular: schema 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | description: Schema defines json document schemas. 22 | properties: 23 | apiVersion: 24 | description: 'APIVersion defines the versioned schema of this representation 25 | of an object. Servers should convert recognized schemas to the latest 26 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 27 | type: string 28 | kind: 29 | description: 'Kind is a string value representing the REST resource this 30 | object represents. Servers may infer this from the endpoint the client 31 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 32 | type: string 33 | metadata: 34 | type: object 35 | spec: 36 | description: SchemaSpec defines the desired state of the resource. 37 | properties: 38 | domain: 39 | description: Domain 40 | type: string 41 | subject: 42 | description: Subject 43 | type: string 44 | variant: 45 | description: Variant 46 | type: string 47 | versions: 48 | description: Versions 49 | items: 50 | description: SchemaVersion defines each version of a schema. 51 | properties: 52 | definition: 53 | description: Definition is the (jsd) json-schema definition. 54 | type: object 55 | x-kubernetes-preserve-unknown-fields: true 56 | migration: 57 | description: Migration defines a yq query to migrate the document. 58 | type: string 59 | required: 60 | - definition 61 | type: object 62 | type: array 63 | required: 64 | - domain 65 | - subject 66 | - variant 67 | - versions 68 | type: object 69 | status: 70 | description: SchemaStatus defines the observed state of the resource. 71 | properties: 72 | observedGeneration: 73 | description: The most recent generation observed by the controller. 74 | format: int64 75 | type: integer 76 | type: object 77 | required: 78 | - spec 79 | type: object 80 | served: true 81 | storage: true 82 | subresources: 83 | status: {} 84 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-schema.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Schema 3 | apiVersion: tackle.konveyor.io/v1alpha1 4 | metadata: 5 | name: cloudfoundry-coordinates 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/part-of: {{ app_name }} 9 | spec: 10 | domain: platform 11 | variant: cloudfoundry 12 | subject: coordinates 13 | versions: 14 | - definition: 15 | '$schema': https://json-schema.org/draft/2020-12/schema 16 | properties: 17 | name: 18 | description: Application name. 19 | minLength: 1 20 | type: string 21 | space: 22 | description: Space name. 23 | minLength: 1 24 | type: string 25 | required: 26 | - space 27 | - name 28 | title: Coordinates 29 | type: object 30 | - definition: 31 | '$schema': https://json-schema.org/draft/2020-12/schema 32 | properties: 33 | organization: 34 | description: Organization name. 35 | minLength: 1 36 | type: string 37 | space: 38 | description: Space name. 39 | minLength: 1 40 | type: string 41 | name: 42 | description: Application name. 43 | minLength: 1 44 | type: string 45 | required: 46 | - organization 47 | - space 48 | - name 49 | title: Coordinates 50 | type: object 51 | migration: > 52 | .organization = "unknown" 53 | --- 54 | kind: Schema 55 | apiVersion: tackle.konveyor.io/v1alpha1 56 | metadata: 57 | name: cloudfoundry-filter 58 | namespace: {{ app_namespace }} 59 | labels: 60 | app.kubernetes.io/part-of: {{ app_name }} 61 | spec: 62 | domain: platform 63 | variant: cloudfoundry 64 | subject: filter 65 | versions: 66 | - definition: 67 | $schema: https://json-schema.org/draft/2020-12/schema 68 | title: Coordinates 69 | type: object 70 | properties: 71 | names: 72 | description: Application names. Each may be a glob expression. 73 | type: array 74 | items: 75 | type: string 76 | minLength: 1 77 | minItems: 0 78 | spaces: 79 | description: Space names. 80 | type: array 81 | items: 82 | type: string 83 | minLength: 1 84 | minItems: 1 85 | required: 86 | - spaces 87 | - definition: 88 | $schema: https://json-schema.org/draft/2020-12/schema 89 | title: Coordinates 90 | type: object 91 | properties: 92 | organizations: 93 | description: Organization names. 94 | type: array 95 | items: 96 | type: string 97 | minLength: 1 98 | minItems: 1 99 | spaces: 100 | description: Space names. 101 | type: array 102 | items: 103 | type: string 104 | minLength: 1 105 | names: 106 | description: Application names. Each may be a glob expression. 107 | type: array 108 | items: 109 | type: string 110 | minLength: 1 111 | -------------------------------------------------------------------------------- /bundle/manifests/tackle.konveyor.io_schemas.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.10.0 6 | creationTimestamp: null 7 | name: schemas.tackle.konveyor.io 8 | spec: 9 | group: tackle.konveyor.io 10 | names: 11 | kind: Schema 12 | listKind: SchemaList 13 | plural: schemas 14 | singular: schema 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Schema defines json document schemas. 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: SchemaSpec defines the desired state of the resource. 36 | properties: 37 | domain: 38 | description: Domain 39 | type: string 40 | subject: 41 | description: Subject 42 | type: string 43 | variant: 44 | description: Variant 45 | type: string 46 | versions: 47 | description: Versions 48 | items: 49 | description: SchemaVersion defines each version of a schema. 50 | properties: 51 | definition: 52 | description: Definition is the (jsd) json-schema definition. 53 | type: object 54 | x-kubernetes-preserve-unknown-fields: true 55 | migration: 56 | description: Migration defines a yq query to migrate the document. 57 | type: string 58 | required: 59 | - definition 60 | type: object 61 | type: array 62 | required: 63 | - domain 64 | - subject 65 | - variant 66 | - versions 67 | type: object 68 | status: 69 | description: SchemaStatus defines the observed state of the resource. 70 | properties: 71 | observedGeneration: 72 | description: The most recent generation observed by the controller. 73 | format: int64 74 | type: integer 75 | type: object 76 | required: 77 | - spec 78 | type: object 79 | served: true 80 | storage: true 81 | subresources: 82 | status: {} 83 | status: 84 | acceptedNames: 85 | kind: "" 86 | plural: "" 87 | conditions: null 88 | storedVersions: null 89 | -------------------------------------------------------------------------------- /roles/tackle/tasks/kai.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Get API key secret 4 | k8s_info: 5 | api_version: v1 6 | kind: Secret 7 | name: "{{ kai_api_key_secret_name }}" 8 | namespace: "{{ app_namespace }}" 9 | register: kai_api_key_secret_status 10 | 11 | - when: kai_llm_proxy_enabled|bool or kai_solution_server_enabled|bool 12 | block: 13 | 14 | - name: Check if DB secret is defined 15 | k8s_info: 16 | api_version: v1 17 | kind: Secret 18 | name: "{{ kai_database_secret_name }}" 19 | namespace: "{{ app_namespace }}" 20 | register: kai_db_secret_status 21 | 22 | - name: Generate random password for Postgres 23 | set_fact: 24 | pg_password: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}" 25 | when: (kai_db_secret_status.resources|length) == 0 26 | 27 | - name: Create DB secret 28 | k8s: 29 | state: present 30 | definition: 31 | apiVersion: v1 32 | kind: Secret 33 | metadata: 34 | name: "{{ kai_database_secret_name }}" 35 | namespace: "{{ app_namespace }}" 36 | stringData: 37 | POSTGRESQL_HOST: "{{ kai_database_address }}" 38 | POSTGRESQL_DATABASE: kai 39 | POSTGRESQL_PASSWORD: "{{ pg_password }}" 40 | POSTGRESQL_USER: kai 41 | when: (kai_db_secret_status.resources|length) == 0 42 | 43 | - name: Decode pg_password from secret 44 | set_fact: 45 | pg_password: "{{ kai_db_secret_status.resources.0.data.POSTGRESQL_PASSWORD | b64decode }}" 46 | when: (kai_db_secret_status.resources|length) > 0 47 | 48 | - name: Deploy KAI DB 49 | k8s: 50 | state: present 51 | template: kai/kai-db-deployment.yaml.j2 52 | 53 | - name: Create KAI DB Service 54 | k8s: 55 | state: present 56 | template: kai/kai-db-service.yaml.j2 57 | 58 | - name: Create KAI DB PersistentVolumeClaim 59 | k8s: 60 | state: present 61 | template: kai/kai-db-pvc.yaml.j2 62 | 63 | 64 | - name: Deploy LLM Proxy 65 | when: kai_llm_proxy_enabled | bool 66 | block: 67 | - name: Create LLM Proxy ConfigMap 68 | k8s: 69 | state: present 70 | template: kai/llm-proxy-configmap.yaml.j2 71 | 72 | - name: Create LLM Proxy Deployment 73 | k8s: 74 | state: present 75 | template: kai/llm-proxy-deployment.yaml.j2 76 | 77 | - name: Create LLM Proxy Service 78 | k8s: 79 | state: present 80 | template: kai/llm-proxy-service.yaml.j2 81 | 82 | - name: Create LLM Proxy Client Configuration 83 | k8s: 84 | state: present 85 | template: kai/llm-proxy-client-configmap.yaml.j2 86 | 87 | - name: Deploy Solution server 88 | when: kai_solution_server_enabled | bool 89 | block: 90 | - name: Create Kai API deployment 91 | k8s: 92 | state: present 93 | template: kai/kai-api-deployment.yaml.j2 94 | merge_type: merge 95 | 96 | - name: Create KAI API Service 97 | k8s: 98 | state: present 99 | template: kai/kai-api-service.yaml.j2 100 | 101 | - name: Update Kai component status conditions 102 | when: ansible_operator_meta is defined 103 | import_tasks: kai-status.yml 104 | -------------------------------------------------------------------------------- /.github/actions/install-tackle/action.yml: -------------------------------------------------------------------------------- 1 | name: Install tackle operator 2 | description: | 3 | Install Tackle Operator. 4 | inputs: 5 | operator-bundle-image: 6 | description: "image url for operator bundle container image" 7 | required: false 8 | default: "quay.io/konveyor/tackle2-operator-bundle:latest" 9 | hub-image: 10 | description: "image url for tackle-hub" 11 | required: false 12 | default: "quay.io/konveyor/tackle2-hub:latest" 13 | ui-image: 14 | description: "image url for tackle-ui" 15 | required: false 16 | default: "quay.io/konveyor/tackle2-ui:latest" 17 | addon-analyzer-image: 18 | description: "image url for analyzer addon" 19 | required: false 20 | default: "quay.io/konveyor/tackle2-addon-analyzer:latest" 21 | java-provider-image: 22 | description: "image url for java provider" 23 | required: false 24 | default: "quay.io/konveyor/java-external-provider:latest" 25 | image-pull-policy: 26 | description: "Image Pull Policy" 27 | required: false 28 | default: "Always" 29 | analyzer-container-cpu: 30 | description: "The CPU request value for the analyzer task containers" 31 | required: false 32 | default: 0 33 | analyzer-container-memory: 34 | description: "The memory request value for the analyzer task containers" 35 | required: false 36 | default: 0 37 | enable_auth: 38 | description: "Enable tackle with auth" 39 | required: false 40 | default: "false" 41 | disable_maven_search: 42 | description: "Disable maven central search" 43 | required: false 44 | default: "true" 45 | runs: 46 | using: "composite" 47 | steps: 48 | - name: Install kubectl 49 | shell: bash 50 | run: | 51 | if command -v kubectl >/dev/null 2>&1; then 52 | echo "kubectl is already installed...yay" 53 | exit 0 54 | fi 55 | curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" 56 | sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl 57 | - name: Install operator-sdk 58 | shell: bash 59 | run: | 60 | if command -v operator-sdk >/dev/null 2>&1; then 61 | echo "operator-sdk is already installed...yay" 62 | exit 0 63 | fi 64 | curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.35.0/operator-sdk_linux_amd64 65 | sudo install -o root -g root -m 0755 operator-sdk_linux_amd64 /usr/local/bin/operator-sdk 66 | - name: Install tackle 67 | run: | 68 | export OPERATOR_BUNDLE_IMAGE="${{ inputs.operator-bundle-image }}" 69 | export HUB_IMAGE="${{ inputs.hub-image }}" 70 | export UI_IMAGE="${{ inputs.ui-image }}" 71 | export ADDON_ANALYZER_IMAGE="${{ inputs.addon-analyzer-image }}" 72 | export JAVA_PROVIDER_IMAGE="${{ inputs.java-provider-image }}" 73 | export IMAGE_PULL_POLICY="${{ inputs.image-pull-policy }}" 74 | export ANALYZER_CONTAINER_REQUESTS_MEMORY="${{ inputs.analyzer-container-memory }}" 75 | export ANALYZER_CONTAINER_REQUESTS_CPU="${{ inputs.analyzer-container-cpu }}" 76 | export DISABLE_MAVEN_SEARCH=${{ inputs.disable_maven_search }} 77 | if [[ "true" == "${{ inputs.enable_auth }}" ]]; then 78 | export FEATURE_AUTH_REQUIRED=true 79 | fi 80 | make install-tackle 81 | working-directory: ${{ github.action_path }}/../../.. 82 | shell: bash 83 | - name: Upload logs on fail 84 | if: ${{ failure() }} 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: debug-output 88 | path: /tmp/konveyor-debug 89 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/llm-proxy-deployment.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: llm-proxy 6 | namespace: "{{ app_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: llm-proxy 9 | app.kubernetes.io/component: kai 10 | app.kubernetes.io/part-of: tackle 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: llm-proxy 16 | app.kubernetes.io/name: llm-proxy 17 | template: 18 | metadata: 19 | labels: 20 | app: llm-proxy 21 | app.kubernetes.io/name: llm-proxy 22 | app.kubernetes.io/component: kai 23 | app.kubernetes.io/part-of: tackle 24 | annotations: 25 | # Force pod restart when ConfigMap changes 26 | checksum/config: "{{ lookup('template', 'kai/llm-proxy-configmap.yaml.j2') | hash('sha256') }}" 27 | spec: 28 | containers: 29 | - name: llm-proxy 30 | image: "{{ kai_llm_proxy_image_fqin }}" 31 | ports: 32 | - containerPort: 8321 33 | name: http 34 | protocol: TCP 35 | startupProbe: 36 | httpGet: 37 | path: /v1/health 38 | port: 8321 39 | failureThreshold: 30 40 | periodSeconds: 10 41 | initialDelaySeconds: 10 42 | livenessProbe: 43 | httpGet: 44 | path: /v1/health 45 | port: 8321 46 | periodSeconds: 30 47 | timeoutSeconds: 5 48 | failureThreshold: 3 49 | readinessProbe: 50 | httpGet: 51 | path: /v1/health 52 | port: 8321 53 | periodSeconds: 10 54 | timeoutSeconds: 5 55 | successThreshold: 1 56 | failureThreshold: 3 57 | env: 58 | - name: RUN_CONFIG_PATH 59 | value: /app/run.yaml 60 | - name: LLAMA_STACK_CONFIG_DIR 61 | value: /tmp/.llama 62 | {% if trusted_ca_enabled | default(false) | bool %} 63 | # For self-signed certificates 64 | - name: SSL_CERT_FILE 65 | value: /etc/pki/ca-trust/source/anchors/ca.crt 66 | - name: REQUESTS_CA_BUNDLE 67 | value: /etc/pki/ca-trust/source/anchors/ca.crt 68 | {% endif %} 69 | - name: POSTGRESQL_PASSWORD 70 | valueFrom: 71 | secretKeyRef: 72 | name: "{{ kai_database_secret_name }}" 73 | key: POSTGRESQL_PASSWORD 74 | {% if kai_api_key_secret_status.resources | length > 0 %} 75 | {% for (key, value) in kai_api_key_secret_status.resources.0.data.items() %} 76 | - name: {{ key }} 77 | valueFrom: 78 | secretKeyRef: 79 | name: "{{ kai_api_key_secret_name }}" 80 | key: {{ key }} 81 | {% endfor %} 82 | {% endif %} 83 | volumeMounts: 84 | - name: config 85 | mountPath: /app 86 | readOnly: true 87 | - name: tmp 88 | mountPath: /tmp 89 | {% if trusted_ca_enabled | default(false) | bool %} 90 | - name: trusted-ca 91 | mountPath: /etc/pki/ca-trust/source/anchors 92 | readOnly: true 93 | {% endif %} 94 | resources: 95 | requests: 96 | memory: "256Mi" 97 | cpu: "100m" 98 | limits: 99 | memory: "512Mi" 100 | cpu: "500m" 101 | volumes: 102 | - name: config 103 | configMap: 104 | name: llm-proxy 105 | - name: tmp 106 | emptyDir: {} 107 | {% if trusted_ca_enabled | default(false) | bool %} 108 | - name: trusted-ca 109 | configMap: 110 | name: trusted-ca 111 | items: 112 | - key: ca-bundle.crt 113 | path: ca.crt 114 | optional: true 115 | {% endif %} 116 | -------------------------------------------------------------------------------- /roles/tackle/templates/deployment-keycloak-postgresql.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ keycloak_database_deployment_name }}-{{ keycloak_database_db_version }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ keycloak_database_service_name }}-{{ keycloak_database_db_version }} 9 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | version: "{{ keycloak_database_db_version }}" 12 | spec: 13 | replicas: {{ keycloak_database_deployment_replicas }} 14 | selector: 15 | matchLabels: 16 | app.kubernetes.io/name: {{ keycloak_database_service_name }}-{{ keycloak_database_db_version }} 17 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 18 | app.kubernetes.io/part-of: {{ app_name }} 19 | version: "{{ keycloak_database_db_version }}" 20 | {% if keycloak_database_deployment_strategy == 'Recreate' %} 21 | strategy: 22 | type: {{ keycloak_database_deployment_strategy }} 23 | {% endif %} 24 | template: 25 | metadata: 26 | labels: 27 | app.kubernetes.io/name: {{ keycloak_database_service_name }}-{{ keycloak_database_db_version }} 28 | app.kubernetes.io/component: {{ keycloak_database_component_name }} 29 | app.kubernetes.io/part-of: {{ app_name }} 30 | app: {{ app_name }} 31 | role: {{ keycloak_database_service_name }} 32 | version: "{{ keycloak_database_db_version }}" 33 | spec: 34 | containers: 35 | - name: {{ keycloak_database_container_name }} 36 | image: "{{ keycloak_database_image_fqin }}" 37 | imagePullPolicy: "{{ image_pull_policy }}" 38 | env: 39 | - name: POSTGRESQL_USER 40 | valueFrom: 41 | secretKeyRef: 42 | name: {{ keycloak_database_secret_name }} 43 | key: database-user 44 | - name: POSTGRESQL_PASSWORD 45 | valueFrom: 46 | secretKeyRef: 47 | name: {{ keycloak_database_secret_name }} 48 | key: database-password 49 | - name: POSTGRESQL_DATABASE 50 | valueFrom: 51 | secretKeyRef: 52 | name: {{ keycloak_database_secret_name }} 53 | key: database-name 54 | ports: 55 | - containerPort: 5432 56 | protocol: TCP 57 | resources: 58 | limits: 59 | cpu: {{ keycloak_database_container_limits_cpu }} 60 | memory: {{ keycloak_database_container_limits_memory }} 61 | requests: 62 | cpu: {{ keycloak_database_container_requests_cpu }} 63 | memory: {{ keycloak_database_container_requests_memory }} 64 | volumeMounts: 65 | - name: {{ keycloak_database_data_volume_name }} 66 | mountPath: {{ keycloak_database_data_volume_path }} 67 | livenessProbe: 68 | exec: 69 | command: 70 | - "/bin/sh" 71 | - "-c" 72 | - 'psql -U $POSTGRESQL_USER -d $POSTGRESQL_DATABASE -c ''SELECT 1'' ' 73 | initialDelaySeconds: 60 74 | timeoutSeconds: 10 75 | periodSeconds: 10 76 | successThreshold: 1 77 | failureThreshold: 3 78 | readinessProbe: 79 | exec: 80 | command: 81 | - "/bin/sh" 82 | - "-c" 83 | - 'psql -U $POSTGRESQL_USER -d $POSTGRESQL_DATABASE -c ''SELECT 1'' ' 84 | initialDelaySeconds: 10 85 | timeoutSeconds: 1 86 | periodSeconds: 10 87 | successThreshold: 1 88 | failureThreshold: 3 89 | terminationMessagePath: "/dev/termination-log" 90 | terminationMessagePolicy: File 91 | {% if not openshift_cluster %} 92 | securityContext: 93 | fsGroup: 26 94 | {% endif %} 95 | volumes: 96 | - name: {{ keycloak_database_data_volume_name }} 97 | persistentVolumeClaim: 98 | claimName: {{ keycloak_database_data_volume_claim_name }} 99 | -------------------------------------------------------------------------------- /roles/tackle/templates/kai/llm-proxy-configmap.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: llm-proxy 6 | namespace: "{{ app_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: llm-proxy 9 | app.kubernetes.io/component: kai 10 | app.kubernetes.io/part-of: tackle 11 | data: 12 | run.yaml: | 13 | version: 2 14 | image_name: starter 15 | external_providers_dir: /tmp/.llama/providers.d 16 | apis: 17 | - inference 18 | providers: 19 | inference: 20 | - provider_id: {{ kai_llm_proxy_provider_id }} 21 | provider_type: {{ kai_llm_proxy_provider_type }} 22 | config: 23 | # All config values use environment variable substitution 24 | # The deployment injects these from the kai-api-keys secret 25 | {% if kai_llm_proxy_provider_type == 'remote::openai' %} 26 | api_key: ${env.OPENAI_API_KEY} 27 | {% if kai_llm_baseurl %} 28 | base_url: {{ kai_llm_baseurl }} 29 | {% endif %} 30 | {% elif kai_llm_proxy_provider_type == 'remote::azure-openai' %} 31 | api_key: ${env.AZURE_OPENAI_API_KEY} 32 | {% if kai_llm_baseurl %} 33 | base_url: {{ kai_llm_baseurl }} 34 | {% else %} 35 | base_url: ${env.AZURE_OPENAI_ENDPOINT} 36 | {% endif %} 37 | api_version: ${env.AZURE_OPENAI_API_VERSION:2024-02-15-preview} 38 | {% elif kai_llm_proxy_provider_type == 'remote::google' %} 39 | api_key: ${env.GOOGLE_API_KEY} 40 | {% if kai_llm_baseurl %} 41 | base_url: {{ kai_llm_baseurl }} 42 | {% endif %} 43 | {% elif kai_llm_proxy_provider_type == 'remote::anthropic' %} 44 | api_key: ${env.ANTHROPIC_API_KEY} 45 | {% if kai_llm_baseurl %} 46 | base_url: {{ kai_llm_baseurl }} 47 | {% endif %} 48 | {% elif kai_llm_proxy_provider_type == 'remote::bedrock' %} 49 | aws_access_key_id: ${env.AWS_ACCESS_KEY_ID} 50 | aws_secret_access_key: ${env.AWS_SECRET_ACCESS_KEY} 51 | aws_session_token: ${env.AWS_SESSION_TOKEN} 52 | region: ${env.AWS_DEFAULT_REGION:us-east-1} 53 | {% else %} 54 | # Generic provider - expects API_KEY and optionally BASE_URL in the secret 55 | api_key: ${env.API_KEY} 56 | {% if kai_llm_baseurl %} 57 | base_url: {{ kai_llm_baseurl }} 58 | {% endif %} 59 | {% endif %} 60 | storage: 61 | backends: 62 | kv_default: 63 | type: kv_postgres 64 | user: kai 65 | password: ${env.POSTGRESQL_PASSWORD} 66 | host: {{ kai_database_address }} 67 | port: 5432 68 | db: kai 69 | table_prefix: llm_proxy_kv_ 70 | sql_default: 71 | type: sql_postgres 72 | user: kai 73 | password: ${env.POSTGRESQL_PASSWORD} 74 | host: {{ kai_database_address }} 75 | port: 5432 76 | db: kai 77 | table_prefix: llm_proxy_ 78 | stores: 79 | metadata: 80 | namespace: registry 81 | backend: kv_default 82 | inference: 83 | table_name: llm_proxy_inference_store 84 | backend: sql_default 85 | max_write_queue_size: 10000 86 | num_writers: 4 87 | conversations: 88 | table_name: llm_proxy_conversations 89 | backend: sql_default 90 | registered_resources: 91 | models: 92 | {% if kai_llm_model %} 93 | - model_id: proxied-model 94 | provider_id: {{ kai_llm_proxy_provider_id }} 95 | model_type: llm 96 | provider_model_id: {{ kai_llm_model }} 97 | {% else %} 98 | [] 99 | {% endif %} 100 | shields: [] 101 | vector_dbs: [] 102 | datasets: [] 103 | scoring_fns: [] 104 | benchmarks: [] 105 | tool_groups: [] 106 | server: 107 | port: 8321 108 | {% if feature_auth_required %} 109 | auth: 110 | provider_config: 111 | type: "oauth2_token" 112 | # Skip TLS verification for self-signed certificates (same as hub does) 113 | verify_tls: false 114 | jwks: 115 | # Use the same protocol and base URL that the hub uses for Keycloak 116 | uri: "{{ keycloak_sso_url }}/auth/realms/{{ keycloak_sso_realm }}/protocol/openid-connect/certs" 117 | # The issuer must match exactly what's in the JWT token from hub auth 118 | issuer: "{{ keycloak_sso_url }}/auth/realms/{{ keycloak_sso_realm }}" 119 | audience: "{{ keycloak_api_audience }}" 120 | {% endif %} 121 | telemetry: 122 | enabled: false -------------------------------------------------------------------------------- /roles/tackle/tasks/kai-status.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file updates the CR status conditions for Kai components 3 | # It only runs when invoked through the operator (ansible_operator_meta is defined) 4 | 5 | - name: Check LLM Proxy deployment status 6 | when: kai_llm_proxy_enabled | bool 7 | k8s_info: 8 | api_version: apps/v1 9 | kind: Deployment 10 | name: llm-proxy 11 | namespace: "{{ app_namespace }}" 12 | register: kai_llm_proxy_deployment 13 | 14 | - name: Check Kai API deployment status 15 | k8s_info: 16 | api_version: apps/v1 17 | kind: Deployment 18 | name: kai-api 19 | namespace: "{{ app_namespace }}" 20 | register: kai_api_deployment 21 | 22 | # Determine proxy readiness - check that deployment is fully ready and updated 23 | - name: Set LLM proxy ready status 24 | set_fact: 25 | kai_llm_proxy_ready: "{{ kai_llm_proxy_enabled | bool and 26 | kai_llm_proxy_deployment.resources | length > 0 and 27 | kai_llm_proxy_deployment.resources[0].status.readyReplicas | default(0) > 0 and 28 | kai_llm_proxy_deployment.resources[0].status.readyReplicas | default(0) == kai_llm_proxy_deployment.resources[0].status.replicas | default(1) and 29 | kai_llm_proxy_deployment.resources[0].status.updatedReplicas | default(0) == kai_llm_proxy_deployment.resources[0].status.replicas | default(1) }}" 30 | 31 | # Determine if Kai is blocked by proxy 32 | - name: Set Kai blocked status 33 | set_fact: 34 | kai_blocked_by_proxy: "{{ kai_llm_proxy_enabled | bool and not (kai_llm_proxy_ready | default(false)) }}" 35 | kai_deployment_ready: "{{ kai_api_deployment.resources | length > 0 and 36 | kai_api_deployment.resources[0].status.readyReplicas | default(0) > 0 and 37 | kai_api_deployment.resources[0].status.readyReplicas | default(0) == kai_api_deployment.resources[0].status.replicas | default(1) and 38 | kai_api_deployment.resources[0].status.updatedReplicas | default(0) == kai_api_deployment.resources[0].status.replicas | default(1) }}" 39 | 40 | - name: Update LLM Proxy condition 41 | when: kai_llm_proxy_enabled | bool 42 | operator_sdk.util.k8s_status: 43 | api_version: tackle.konveyor.io/v1alpha1 44 | kind: Tackle 45 | name: "{{ ansible_operator_meta.name }}" 46 | namespace: "{{ ansible_operator_meta.namespace }}" 47 | conditions: 48 | - type: LLMProxyReady 49 | status: "{{ 'True' if kai_llm_proxy_ready else 'False' }}" 50 | reason: "{{ 'DeploymentReady' if kai_llm_proxy_ready else 'DeploymentNotReady' }}" 51 | message: "{{ 'LLM Proxy is running and ready' if kai_llm_proxy_ready 52 | else 'LLM Proxy deployment is not ready. Check pod logs for details.' }}" 53 | lastTransitionTime: "{{ now(fmt='%Y-%m-%dT%H:%M:%SZ') }}" 54 | 55 | - name: Update Kai Solution Server condition 56 | operator_sdk.util.k8s_status: 57 | api_version: tackle.konveyor.io/v1alpha1 58 | kind: Tackle 59 | name: "{{ ansible_operator_meta.name }}" 60 | namespace: "{{ ansible_operator_meta.namespace }}" 61 | conditions: 62 | - type: KaiSolutionServerReady 63 | status: >- 64 | {{ 'False' if kai_blocked_by_proxy else 65 | ('True' if kai_deployment_ready else 'False') }} 66 | reason: >- 67 | {{ 'WaitingForLLMProxy' if kai_blocked_by_proxy else 68 | ('DeploymentReady' if kai_deployment_ready else 'DeploymentNotReady') }} 69 | message: >- 70 | {{ 'Waiting for LLM Proxy to become ready' if kai_blocked_by_proxy else 71 | ('Kai Solution Server is running and ready' if kai_deployment_ready else 72 | 'Kai Solution Server deployment is not ready. Check pod logs for details.') }} 73 | lastTransitionTime: "{{ now(fmt='%Y-%m-%dT%H:%M:%SZ') }}" 74 | 75 | - name: Update Kai API Keys condition 76 | operator_sdk.util.k8s_status: 77 | api_version: tackle.konveyor.io/v1alpha1 78 | kind: Tackle 79 | name: "{{ ansible_operator_meta.name }}" 80 | namespace: "{{ ansible_operator_meta.namespace }}" 81 | conditions: 82 | - type: KaiAPIKeysConfigured 83 | status: "{{ 'True' if (kai_api_key_secret_status.resources | length > 0) else 'False' }}" 84 | reason: "{{ 'SecretFound' if (kai_api_key_secret_status.resources | length > 0) else 'SecretNotFound' }}" 85 | message: "{{ 'API keys secret is configured' if (kai_api_key_secret_status.resources | length > 0) 86 | else 'No API keys secret found. Some LLM providers may not work without authentication.' }}" 87 | lastTransitionTime: "{{ now(fmt='%Y-%m-%dT%H:%M:%SZ') }}" 88 | -------------------------------------------------------------------------------- /.github/actions/make-bundle/action.yml: -------------------------------------------------------------------------------- 1 | name: Make Operator Bundle 2 | description: | 3 | Make an operator bundle. This does not save the image for you. 4 | inputs: 5 | operator_bundle: 6 | description: "image uri for operator bundle (ie. quay.io//:)" 7 | required: true 8 | operator: 9 | description: "image uri for operator (ie. quay.io//:)" 10 | required: false 11 | default: "" 12 | oauth_proxy: 13 | description: "image uri for oauth_proxy (ie. quay.io//:)" 14 | required: false 15 | default: "" 16 | tackle_hub: 17 | description: "image uri for tackle-hub (ie. quay.io//:)" 18 | required: false 19 | default: "" 20 | kai: 21 | description: "image uri for kai (ie quay.io//:)" 22 | required: false 23 | default: "" 24 | tackle_postgres: 25 | description: "image uri for tackle-postgres (ie. quay.io//:)" 26 | required: false 27 | default: "" 28 | keycloak_sso: 29 | description: "image uri for keycloak_sso image (ie. quay.io//:)" 30 | required: false 31 | default: "" 32 | keycloak_init: 33 | description: "image uri for keycloak_init image (ie. quay.io//:)" 34 | required: false 35 | default: "" 36 | tackle_ui: 37 | description: "image uri for tackle-ui (ie. quay.io//:)" 38 | required: false 39 | default: "" 40 | addon_analyzer: 41 | description: "image uri for analyzer addon (ie. quay.io//:)" 42 | required: false 43 | default: "" 44 | addon_discovery: 45 | description: "image uri for discovery addon (ie. quay.io//:)" 46 | required: false 47 | default: "" 48 | provider_generic: 49 | description: "image uri for generic provider (ie. quay.io//:)" 50 | required: false 51 | default: "" 52 | provider_java: 53 | description: "image uri for generic provider (ie. quay.io//:)" 54 | required: false 55 | default: "" 56 | provider_c_sharp: 57 | description: "image uri for c-sharp provider (ie. quay.io//:)" 58 | required: false 59 | default: "" 60 | kantra: 61 | description: "image uri for kantra (ie. quay.io//:)" 62 | required: false 63 | default: "" 64 | version: 65 | description: "operator version" 66 | required: false 67 | default: "" 68 | channels: 69 | description: "comma separated channel(s) this operator should be available on" 70 | required: false 71 | default: "" 72 | push_bundle: 73 | description: "" 74 | required: false 75 | default: "false" 76 | 77 | runs: 78 | using: "composite" 79 | steps: 80 | - name: Make bundle 81 | env: 82 | BUNDLE_IMG: ${{ inputs.operator_bundle }} 83 | run: | 84 | [ -n "${{ inputs.channels }}" ] && export CHANNELS="${{ inputs.channels }}" 85 | [ -n "${{ inputs.version }}" ] && export VERSION="${{ inputs.version }}" 86 | [ -n "${VERSION}" ] && export VERSION="${VERSION:1}" 87 | 88 | OPTS="" 89 | [ -n "${{ inputs.operator }}" ] && OPTS+=" --set images.operator=${{ inputs.operator }}" 90 | [ -n "${{ inputs.oauth_proxy }}" ] && OPTS+=" --set images.oauth_proxy=${{ inputs.oauth_proxy }}" 91 | [ -n "${{ inputs.tackle_hub }}" ] && OPTS+=" --set images.tackle_hub=${{ inputs.tackle_hub }}" 92 | [ -n "${{ inputs.kai }}" ] && OPTS+=" --set images.kai=${{ inputs.kai }}" 93 | [ -n "${{ inputs.tackle_postgres }}" ] && OPTS+=" --set images.tackle_postgres=${{ inputs.tackle_postgres }}" 94 | [ -n "${{ inputs.keycloak_sso }}" ] && OPTS+=" --set images.keycloak_sso=${{ inputs.keycloak_sso }}" 95 | [ -n "${{ inputs.keycloak_init }}" ] && OPTS+=" --set images.keycloak_init=${{ inputs.keycloak_init }}" 96 | [ -n "${{ inputs.tackle_ui }}" ] && OPTS+=" --set images.tackle_ui=${{ inputs.tackle_ui }}" 97 | [ -n "${{ inputs.addon_analyzer }}" ] && OPTS+=" --set images.addon_analyzer=${{ inputs.addon_analyzer }}" 98 | [ -n "${{ inputs.addon_discovery }}" ] && OPTS+=" --set images.addon_discovery=${{ inputs.addon_discovery }}" 99 | [ -n "${{ inputs.provider_generic }}" ] && OPTS+=" --set images.provider_generic=${{ inputs.provider_generic }}" 100 | [ -n "${{ inputs.provider_java }}" ] && OPTS+=" --set images.provider_java=${{ inputs.provider_java }}" 101 | [ -n "${{ inputs.provider_c_sharp }}" ] && OPTS+=" --set images.provider_c_sharp=${{ inputs.provider_c_sharp }}" 102 | [ -n "${{ inputs.kantra }}" ] && OPTS+=" --set images.kantra=${{ inputs.kantra }}" 103 | HELM_OPTS="${OPTS}" make bundle 104 | cat ./bundle/manifests/konveyor-operator.clusterserviceversion.yaml 105 | make bundle-build 106 | working-directory: ${{ github.action_path }}/../../.. 107 | shell: bash 108 | 109 | - name: Push bundle 110 | if: ${{ inputs.push_bundle == 'true' }} 111 | env: 112 | BUNDLE_IMG: ${{ inputs.operator_bundle }} 113 | run: | 114 | make bundle-push 115 | working-directory: ${{ github.action_path }}/../../.. 116 | shell: bash 117 | -------------------------------------------------------------------------------- /roles/tackle/templates/deployment-keycloak-sso.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ keycloak_sso_deployment_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 9 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | annotations: 12 | app.openshift.io/connects-to: >- 13 | [ 14 | { "apiVersion": "apps/v1", "kind": "Deployment", "name": "{{ keycloak_database_deployment_name }}" } 15 | ] 16 | spec: 17 | replicas: {{ keycloak_sso_deployment_replicas }} 18 | selector: 19 | matchLabels: 20 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 21 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 22 | app.kubernetes.io/part-of: {{ app_name }} 23 | {% if keycloak_sso_deployment_strategy == 'Recreate' %} 24 | strategy: 25 | type: {{ keycloak_sso_deployment_strategy }} 26 | {% endif %} 27 | template: 28 | metadata: 29 | labels: 30 | app.kubernetes.io/name: {{ keycloak_sso_service_name }} 31 | app.kubernetes.io/component: {{ keycloak_sso_component_name }} 32 | app.kubernetes.io/part-of: {{ app_name }} 33 | app: {{ app_name }} 34 | role: {{ keycloak_sso_service_name }} 35 | spec: 36 | initContainers: 37 | - name: keycloak-theme 38 | image: "{{ keycloak_init_image_fqin }}" 39 | volumeMounts: 40 | - name: {{ keycloak_sso_service_name }}-theme 41 | mountPath: /deployments 42 | containers: 43 | - name: {{ keycloak_sso_container_name }} 44 | image: "{{ keycloak_sso_image_fqin }}" 45 | args: 46 | - -Djgroups.dns.query=mta-kc-discovery.openshift-mta 47 | - --verbose 48 | - start 49 | imagePullPolicy: "{{ image_pull_policy }}" 50 | env: 51 | - name: KC_BOOTSTRAP_ADMIN_USERNAME 52 | valueFrom: 53 | secretKeyRef: 54 | name: {{ keycloak_sso_secret_name }} 55 | key: username 56 | - name: KC_BOOTSTRAP_ADMIN_PASSWORD 57 | valueFrom: 58 | secretKeyRef: 59 | name: {{ keycloak_sso_secret_name }} 60 | key: password 61 | - name: JAVA_OPTS 62 | value: {{ keycloak_sso_java_opts }} 63 | - name: PROXY_ADDRESS_FORWARDING 64 | value: 'true' 65 | - name: KC_DB 66 | value: postgres 67 | - name: KC_DB_URL 68 | value: jdbc:postgresql://{{ keycloak_database_service_k8s_resource_name }}:5432/{{ keycloak_database_db_name }} 69 | - name: KC_DB_USERNAME 70 | valueFrom: 71 | secretKeyRef: 72 | name: {{ keycloak_database_secret_name }} 73 | key: database-user 74 | - name: KC_DB_PASSWORD 75 | valueFrom: 76 | secretKeyRef: 77 | name: {{ keycloak_database_secret_name }} 78 | key: database-password 79 | - name: KC_HTTP_RELATIVE_PATH 80 | value: /auth 81 | - name: KC_PROXY_HEADERS 82 | value: xforwarded 83 | {% if keycloak_sso_tls_enabled|bool %} 84 | - name: KC_HTTPS_CERTIFICATE_FILE 85 | value: /service-crt/tls.crt 86 | - name: KC_HTTPS_CERTIFICATE_KEY_FILE 87 | value: /service-crt/tls.key 88 | {% endif %} 89 | - name: KC_HOSTNAME_STRICT 90 | value: "false" 91 | - name: KC_HTTP_ENABLED 92 | value: "true" 93 | ports: 94 | - name: http 95 | containerPort: 8080 96 | protocol: TCP 97 | - name: https 98 | containerPort: 8443 99 | protocol: TCP 100 | resources: 101 | limits: 102 | cpu: {{ keycloak_sso_container_limits_cpu }} 103 | memory: {{ keycloak_sso_container_limits_memory }} 104 | requests: 105 | cpu: {{ keycloak_sso_container_requests_cpu }} 106 | memory: {{ keycloak_sso_container_requests_memory }} 107 | livenessProbe: 108 | httpGet: 109 | path: / 110 | port: {{ keycloak_sso_port }} 111 | scheme: {{ keycloak_sso_proto|upper }} 112 | initialDelaySeconds: {{ keycloak_sso_liveness_init_delay }} 113 | timeoutSeconds: 1 114 | periodSeconds: 10 115 | successThreshold: 1 116 | failureThreshold: 20 117 | readinessProbe: 118 | httpGet: 119 | path: / 120 | port: {{ keycloak_sso_port }} 121 | scheme: {{ keycloak_sso_proto|upper }} 122 | initialDelaySeconds: {{ keycloak_sso_readiness_init_delay }} 123 | timeoutSeconds: 1 124 | periodSeconds: 10 125 | successThreshold: 1 126 | failureThreshold: 20 127 | volumeMounts: 128 | - name: {{ keycloak_sso_service_name }}-theme 129 | mountPath: /opt/jboss/keycloak/standalone/deployments 130 | {% if keycloak_sso_tls_enabled|bool %} 131 | - mountPath: "/service-crt" 132 | name: service-crt 133 | readOnly: true 134 | {% endif %} 135 | volumes: 136 | - name: {{ keycloak_sso_service_name }}-theme 137 | emptyDir: {} 138 | {% if keycloak_sso_tls_enabled|bool %} 139 | - name: service-crt 140 | secret: 141 | secretName: {{ keycloak_sso_tls_secret_name }} 142 | {% endif %} 143 | -------------------------------------------------------------------------------- /hack/install-konveyor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -E 4 | set -e 5 | set -x 6 | set -o pipefail 7 | 8 | NAMESPACE="${NAMESPACE:-konveyor-tackle}" 9 | OPERATOR_BUNDLE_IMAGE="${OPERATOR_BUNDLE_IMAGE:-quay.io/konveyor/tackle2-operator-bundle:latest}" 10 | TACKLE_CR="${TACKLE_CR:-}" 11 | TIMEOUT="${TIMEOUT:-15m}" 12 | OLM_VERSION="${OLM_VERSION:-0.28.0}" 13 | DISABLE_MAVEN_SEARCH="${DISABLE_MAVEN_SEARCH:-false}" 14 | FEATURE_AUTH_REQUIRED="${FEATURE_AUTH_REQUIRED:-false}" 15 | KAI_SOLUTION_SERVER_ENABLED="${KAI_SOLUTION_SERVER_ENABLED:-}" 16 | KAI_LLM_MODEL="${KAI_LLM_MODEL:-}" 17 | KAI_LLM_PROVIDER="${KAI_LLM_PROVIDER:-}" 18 | 19 | if ! command -v kubectl >/dev/null 2>&1; then 20 | echo "Please install kubectl. See https://kubernetes.io/docs/tasks/tools/" 21 | exit 1 22 | fi 23 | 24 | if ! command -v operator-sdk >/dev/null 2>&1; then 25 | echo "Please install operator-sdk. See https://sdk.operatorframework.io/docs/installation/" 26 | exit 1 27 | fi 28 | 29 | debug() { 30 | set +e 31 | echo "Install Konveyor FAILED!!!" 32 | echo "What follows is some info that may be useful in debugging the failure" 33 | 34 | if [ "${CI}" == "true" ]; then 35 | debug_output="/tmp/konveyor-debug" 36 | mkdir -p "${debug_output}" 37 | namespace="${debug_output}/namespace.yaml" 38 | all="${debug_output}/all_resources.yaml" 39 | operator="${debug_output}/operator_resources.yaml" 40 | tackle="${debug_output}/tackle.yaml" 41 | pods="${debug_output}/pod_descriptions.yaml" 42 | 43 | kubectl get namespace "${NAMESPACE}" -o yaml | tee "${namespace}" 44 | kubectl get --namespace "${NAMESPACE}" all | tee "${all}" 45 | kubectl get --namespace "${NAMESPACE}" -o yaml \ 46 | subscriptions.operators.coreos.com,catalogsources.operators.coreos.com,installplans.operators.coreos.com,clusterserviceversions.operators.coreos.com | tee "${operator}" 47 | kubectl get --namespace "${NAMESPACE}" -o yaml tackles.tackle.konveyor.io/tackle | tee "${tackle}" 48 | 49 | for pod in $(kubectl get pods -n "${NAMESPACE}" -o jsonpath='{.items[*].metadata.name}'); do 50 | kubectl --namespace "${NAMESPACE}" describe pod "${pod}" | tee -a "${pods}" 51 | kubectl --namespace "${NAMESPACE}" logs "${pod}" | tee "${debug_output}/${pod}.log" 52 | done 53 | else 54 | kubectl get namespace "${NAMESPACE}" -o yaml 55 | kubectl get --namespace "${NAMESPACE}" all 56 | kubectl get --namespace "${NAMESPACE}" -o yaml \ 57 | subscriptions.operators.coreos.com,catalogsources.operators.coreos.com,installplans.operators.coreos.com,clusterserviceversions.operators.coreos.com 58 | kubectl get --namespace "${NAMESPACE}" -o yaml tackles.tackle.konveyor.io/tackle 59 | 60 | for pod in $(kubectl get pods -n "${NAMESPACE}" -o jsonpath='{.items[*].metadata.name}'); do 61 | kubectl --namespace "${NAMESPACE}" describe pod "${pod}" 62 | done 63 | fi 64 | 65 | exit 1 66 | } 67 | trap 'debug' ERR 68 | 69 | run_bundle() { 70 | kubectl auth can-i create namespace --all-namespaces 71 | kubectl create namespace "${NAMESPACE}" || true 72 | operator-sdk run bundle "${OPERATOR_BUNDLE_IMAGE}" --namespace "${NAMESPACE}" --timeout "${TIMEOUT}" 73 | 74 | # If on MacOS, need to install `brew install coreutils` to get `timeout` 75 | timeout 600s bash -c 'until kubectl get customresourcedefinitions.apiextensions.k8s.io tackles.tackle.konveyor.io; do sleep 30; done' 76 | kubectl get clusterserviceversions.operators.coreos.com -n "${NAMESPACE}" -o yaml 77 | } 78 | 79 | install_tackle() { 80 | echo "Waiting for the Tackle CRD to become available" 81 | kubectl wait --namespace "${NAMESPACE}" --for=condition=established customresourcedefinitions.apiextensions.k8s.io/tackles.tackle.konveyor.io 82 | 83 | echo "Waiting for the Tackle Operator to exist" 84 | timeout 2m bash -c "until kubectl --namespace ${NAMESPACE} get deployment/tackle-operator; do sleep 10; done" 85 | 86 | echo "Waiting for the Tackle Operator to become available" 87 | kubectl rollout status --namespace "${NAMESPACE}" -w deployment/tackle-operator --timeout=600s 88 | 89 | if [ -n "${TACKLE_CR}" ]; then 90 | echo "${TACKLE_CR}" | kubectl apply --namespace "${NAMESPACE}" -f - 91 | else 92 | TACKLE_CR=$(cat <0.3 upgrade Assessments migration script (migrates legacy Pathfinder data). 4 | # 5 | # Based on Konveyor CLI tool https://github.com/konveyor/tackle2-hub/tree/main/hack/tool 6 | # 7 | # Usage: 8 | # $ ./migrate-pathfinder-assessments --pathfinder-url http://pathfinder.svc --hub-base-url http://hub.svc:8080 --token "" 9 | # 10 | 11 | import argparse 12 | import json 13 | import requests 14 | 15 | ############################################################################### 16 | 17 | parser = argparse.ArgumentParser(description='Konveyor Pathfinder Assessments migration script.') 18 | parser.add_argument('-v','--verbose', dest='verbose', action='store_const', const=True, default=False, 19 | help='Print verbose output (including all API requests).') 20 | parser.add_argument('-p','--pathfinder-url', type=str, help='In-cluster Pathfinder endpoint URL.', 21 | nargs='?', default='', required=True) 22 | parser.add_argument('-b','--hub-base-url', type=str, help='In-cluster Hub API endpoint URL.', 23 | nargs='?', default='', required=True) 24 | parser.add_argument('-t','--token', type=str, help='Bearer authorization token.', 25 | nargs='?', default='') 26 | args = parser.parse_args() 27 | 28 | ############################################################################### 29 | 30 | def debugPrint(str): 31 | if args.verbose: 32 | print(str) 33 | 34 | def apiJSON(url, token, data=None, method='GET', ignoreErrors=False): 35 | debugPrint("Querying: %s" % url) 36 | if method == 'DELETE': 37 | r = requests.delete(url, headers={"Authorization": "Bearer %s" % token, "Content-Type": "application/json"}, verify=False) 38 | elif method == 'POST': 39 | debugPrint("POST data: %s" % json.dumps(data)) 40 | r = requests.post(url, data=json.dumps(data), headers={"Authorization": "Bearer %s" % token, "Content-Type": "application/json"}, verify=False) 41 | elif method == 'PATCH': 42 | debugPrint("PATCH data: %s" % json.dumps(data)) 43 | r = requests.patch(url, data=json.dumps(data), headers={"Authorization": "Bearer %s" % token, "Content-Type": "application/json"}, verify=False) 44 | elif method == 'PUT': 45 | debugPrint("PUT data: %s" % json.dumps(data)) 46 | r = requests.put(url, data=json.dumps(data), headers={"Authorization": "Bearer %s" % token, "Content-Type": "application/json"}, verify=False) 47 | else: # GET 48 | r = requests.get(url, headers={"Authorization": "Bearer %s" % token, "Content-Type": "application/json"}, verify=False) 49 | 50 | if not r.ok: 51 | if ignoreErrors: 52 | debugPrint("Got status %d for %s, ignoring" % (r.status_code, url)) 53 | else: 54 | print("ERROR: API request failed with status %d for %s" % (r.status_code, url)) 55 | exit(1) 56 | 57 | if r.text is None or r.text == '': 58 | return 59 | 60 | debugPrint("Response: %s" % r.text) 61 | 62 | respData = json.loads(r.text) 63 | if '_embedded' in respData: 64 | debugPrint("Unwrapping Tackle1 JSON") 65 | return respData['_embedded'][url.rsplit('/')[-1].rsplit('?')[0]] # unwrap JSON response (e.g. _embedded -> application -> [{...}]) 66 | else: 67 | return respData # raw return JSON (Tackle2, Pathfinder) 68 | ############################################################################### 69 | 70 | def migrateAssessments(pathfinder_url, hub_base_url, token): 71 | cnt = 0 72 | apps = apiJSON(hub_base_url + "/applications", token) 73 | print("There are %d Applications, looking for their Assessments.." % len(apps)) 74 | for app in apps: 75 | # If there would be more assessments, only first one is migrated. 76 | for passmnt in apiJSON(pathfinder_url + "/assessments?applicationId=%d" % app['id'], token): 77 | print("# Assessment for Application %s" % passmnt["applicationId"]) 78 | appAssessmentsPath = "/applications/%d/assessments" % passmnt["applicationId"] 79 | # Skip if Assessment for given Application already exists 80 | if len(apiJSON(hub_base_url + appAssessmentsPath, token)) > 0: 81 | print(" Assessment already exists, skipping.") 82 | continue 83 | 84 | # Prepare new Assessment 85 | passmnt = apiJSON(pathfinder_url + "/assessments/%d" % passmnt['id'], token) 86 | assmnt = dict() 87 | assmnt['questionnaire'] = {"id": 1} # Default new Questionnaire "Pathfinder Legacy" 88 | assmnt['application'] = {"id": passmnt["applicationId"]} 89 | assmnt['stakeholders'] = [] 90 | for sh in passmnt['stakeholders']: 91 | assmnt['stakeholders'].append({"id": sh}) 92 | assmnt['stakeholderGroups'] = [] 93 | for shg in passmnt['stakeholderGroups']: 94 | assmnt['stakeholderGroups'].append({"id": shg}) 95 | 96 | # Transformate Questions, Answers and related structures 97 | for category in passmnt['questionnaire']['categories']: 98 | del category['id'] 99 | category['name'] = category.pop('title') 100 | for question in category["questions"]: 101 | del question['id'] 102 | question["text"] = question.pop('question') 103 | question["explanation"] = question.pop('description') 104 | question["answers"] = question.pop('options') 105 | for answer in question['answers']: 106 | del answer['id'] 107 | answer['text'] = answer.pop('option') 108 | answer['selected'] = answer.pop('checked') 109 | answer['risk'] = answer['risk'].lower() 110 | if answer['risk'] == "amber": 111 | answer['risk'] = "yellow" 112 | assmnt['sections'] = passmnt['questionnaire']['categories'] 113 | 114 | # Post the Assessment 115 | apiJSON(hub_base_url + appAssessmentsPath, token, data=assmnt, method='POST') 116 | cnt += 1 117 | print("Assessment submitted.") 118 | return cnt 119 | 120 | 121 | ############################################################################### 122 | 123 | print("Starting Pathfinder Assessments to Konveyor Assessment migration.") 124 | 125 | appCnt = migrateAssessments(args.pathfinder_url, args.hub_base_url, args.token) 126 | 127 | print("Done. %d new Assessment(s) for Application(s) were migrated!" % appCnt) 128 | 129 | ############################################################################### 130 | -------------------------------------------------------------------------------- /roles/tackle/templates/customresource-extension.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Extension 3 | apiVersion: tackle.konveyor.io/v1alpha1 4 | metadata: 5 | name: {{ provider_java_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ provider_java_service_name }} 9 | app.kubernetes.io/component: {{ provider_java_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | spec: 12 | addon: ^({{ analyzer_name }}|{{ tech_discovery_name }})$ 13 | selector: tag:Language=Java || !tag:Language 14 | container: 15 | name: {{ provider_java_name }} 16 | image: {{ provider_java_image_fqin }} 17 | imagePullPolicy: {{ image_pull_policy }} 18 | args: 19 | - --port 20 | - $(PORT) 21 | env: 22 | - name: PORT 23 | value: ${seq:8000} 24 | - name: MAVEN_OPTS 25 | value: -Dmaven.repo.local={{ cache_mount_path }}/m2 26 | - name: JAVA_TOOL_OPTIONS 27 | value: > 28 | -Declipse.log.console=true 29 | -Dorg.eclipse.jdt.ls.log=all 30 | -Dorg.eclipse.jdt.ls.log.level=ALL 31 | resources: 32 | limits: 33 | cpu: {{ provider_java_container_limits_cpu }} 34 | memory: {{ provider_java_container_limits_memory }} 35 | requests: 36 | cpu: {{ provider_java_container_requests_cpu }} 37 | memory: {{ provider_java_container_requests_memory }} 38 | metadata: 39 | resources: 40 | - selector: identity:kind=maven 41 | fields: 42 | - name: settings 43 | path: /shared/creds/maven/settings.xml 44 | key: maven.settings.path 45 | - selector: setting:key=mvn.insecure.enabled 46 | fields: 47 | - name: value 48 | key: maven.insecure 49 | provider: 50 | name: {{ provider_java_name }} 51 | address: localhost:$(PORT) 52 | initConfig: 53 | - providerSpecificConfig: 54 | bundles: /jdtls/java-analyzer-bundle/java-analyzer-bundle.core/target/java-analyzer-bundle.core-1.0.0-SNAPSHOT.jar 55 | mavenIndexPath: /usr/local/etc/maven-index.txt 56 | depOpenSourceLabelsFile: /usr/local/etc/maven.default.index 57 | lspServerPath: /jdtls/bin/jdtls 58 | disableMavenSearch: {{ disable_maven_search }} 59 | mavenInsecure: $(maven.insecure) 60 | mavenSettingsFile: $(maven.settings.path) 61 | mavenCacheDir: {{ cache_mount_path }}/m2 62 | --- 63 | kind: Extension 64 | apiVersion: tackle.konveyor.io/v1alpha1 65 | metadata: 66 | name: {{ provider_python_name }} 67 | namespace: {{ app_namespace }} 68 | labels: 69 | app.kubernetes.io/name: {{ provider_python_service_name }} 70 | app.kubernetes.io/component: {{ provider_python_component_name }} 71 | app.kubernetes.io/part-of: {{ app_name }} 72 | spec: 73 | addon: ^({{ analyzer_name }}|{{ tech_discovery_name }})$ 74 | selector: tag:Language=Golang || tag:Language=Python 75 | container: 76 | name: {{ provider_python_name }} 77 | image: {{ provider_python_image_fqin }} 78 | imagePullPolicy: {{ image_pull_policy }} 79 | args: 80 | - --port 81 | - $(PORT) 82 | env: 83 | - name: PORT 84 | value: ${seq:8000} 85 | resources: 86 | limits: 87 | cpu: {{ provider_python_container_limits_cpu }} 88 | memory: {{ provider_python_container_limits_memory }} 89 | requests: 90 | cpu: {{ provider_python_container_requests_cpu }} 91 | memory: {{ provider_python_container_requests_memory }} 92 | metadata: 93 | provider: 94 | address: localhost:$(PORT) 95 | initConfig: 96 | - providerSpecificConfig: 97 | lspServerName: generic 98 | lspServerPath: /usr/local/bin/pylsp 99 | workspaceFolders: 100 | - $(builtin.location) 101 | dependencyFolders: 102 | - examples/python/__pycache__ 103 | - examples/python/.venv 104 | name: {{ provider_python_name }} 105 | selector: tag:Language=Python 106 | --- 107 | kind: Extension 108 | apiVersion: tackle.konveyor.io/v1alpha1 109 | metadata: 110 | name: {{ provider_nodejs_name }} 111 | namespace: {{ app_namespace }} 112 | labels: 113 | app.kubernetes.io/name: {{ provider_nodejs_service_name }} 114 | app.kubernetes.io/component: {{ provider_nodejs_component_name }} 115 | app.kubernetes.io/part-of: {{ app_name }} 116 | spec: 117 | addon: ^({{ analyzer_name }}|{{ tech_discovery_name }})$ 118 | selector: tag:Language=TypeScript 119 | container: 120 | name: {{ provider_nodejs_name }} 121 | image: {{ provider_nodejs_image_fqin }} 122 | imagePullPolicy: {{ image_pull_policy }} 123 | args: 124 | - --port 125 | - $(PORT) 126 | env: 127 | - name: PORT 128 | value: ${seq:8000} 129 | resources: 130 | limits: 131 | cpu: {{ provider_nodejs_container_limits_cpu }} 132 | memory: {{ provider_nodejs_container_limits_memory }} 133 | requests: 134 | cpu: {{ provider_nodejs_container_requests_cpu }} 135 | memory: {{ provider_nodejs_container_requests_memory }} 136 | metadata: 137 | provider: 138 | address: localhost:$(PORT) 139 | initConfig: 140 | - providerSpecificConfig: 141 | lspServerName: nodejs 142 | lspServerPath: /usr/local/bin/typescript-language-server 143 | lspServerArgs: 144 | - "--stdio" 145 | workspaceFolders: 146 | - $(builtin.location) 147 | name: nodejs 148 | selector: tag:Language=TypeScript||tag:Language=JavaScript 149 | --- 150 | kind: Extension 151 | apiVersion: tackle.konveyor.io/v1alpha1 152 | metadata: 153 | name: {{ provider_c_sharp_name }} 154 | namespace: {{ app_namespace }} 155 | labels: 156 | app.kubernetes.io/name: {{ provider_c_sharp_service_name }} 157 | app.kubernetes.io/component: {{ provider_c_sharp_component_name }} 158 | app.kubernetes.io/part-of: {{ app_name }} 159 | spec: 160 | addon: ^({{ analyzer_name }}|{{ tech_discovery_name }})$ 161 | selector: tag:Language=C# 162 | container: 163 | name: {{ provider_c_sharp_name }} 164 | image: {{ provider_c_sharp_image_fqin }} 165 | imagePullPolicy: {{ image_pull_policy }} 166 | args: 167 | - --port 168 | - $(PORT) 169 | env: 170 | - name: PORT 171 | value: ${seq:8000} 172 | resources: 173 | limits: 174 | cpu: {{ provider_c_sharp_container_limits_cpu }} 175 | memory: {{ provider_c_sharp_container_limits_memory }} 176 | requests: 177 | cpu: {{ provider_c_sharp_container_requests_cpu }} 178 | memory: {{ provider_c_sharp_container_requests_memory }} 179 | metadata: 180 | provider: 181 | name: {{ provider_c_sharp_name }} 182 | address: localhost:$(PORT) 183 | initConfig: 184 | - providerSpecificConfig: 185 | ilspy_cmd: "/usr/local/bin/ilspycmd" 186 | paket_cmd: "/usr/local/bin/paket" 187 | selector: tag:Language=C# 188 | -------------------------------------------------------------------------------- /roles/tackle/templates/deployment-ui.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ ui_deployment_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ ui_service_name }} 9 | app.kubernetes.io/component: {{ ui_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | annotations: 12 | app.openshift.io/connects-to: >- 13 | [ 14 | {% if feature_auth_required|bool and feature_auth_type == "keycloak" and app_profile == 'konveyor' %} 15 | { "apiVersion": "apps/v1", "kind": "Deployment", "name": "{{ keycloak_sso_deployment_name }}" }, 16 | {% elif feature_auth_required|bool and feature_auth_type == "keycloak" and app_profile == 'mta' %} 17 | { "apiVersion": "apps/v1", "kind": "StatefulSet", "name": "keycloak" }, 18 | {% endif %} 19 | { "apiVersion": "apps/v1", "kind": "Deployment", "name": "{{ hub_deployment_name }}" } 20 | ] 21 | spec: 22 | replicas: {{ ui_deployment_replicas }} 23 | selector: 24 | matchLabels: 25 | app.kubernetes.io/name: {{ ui_service_name }} 26 | app.kubernetes.io/component: {{ ui_component_name }} 27 | app.kubernetes.io/part-of: {{ app_name }} 28 | template: 29 | metadata: 30 | labels: 31 | app.kubernetes.io/name: {{ ui_service_name }} 32 | app.kubernetes.io/component: {{ ui_component_name }} 33 | app.kubernetes.io/part-of: {{ app_name }} 34 | app: {{ app_name }} 35 | role: {{ ui_service_name }} 36 | spec: 37 | containers: 38 | {% if feature_auth_required|bool and feature_auth_type == "oauth" %} 39 | - args: 40 | - --https-address=:{{ oauth_ssl_port }} 41 | - --provider={{ oauth_provider }} 42 | - --upstream=http://localhost:{{ ui_port }} 43 | - --cookie-secret={{ cookie_secret_data }} 44 | {% if oauth_provider == "openshift" %} 45 | - --openshift-service-account={{ ui_serviceaccount_name }} 46 | - --tls-cert=/etc/tls/private/tls.crt 47 | - --tls-key=/etc/tls/private/tls.key 48 | {% else %} 49 | - --tls-cert-file=/etc/tls/private/tls.crt 50 | - --tls-key-file=/etc/tls/private/tls.key 51 | {% if oauth_email_domain is defined %} 52 | - --email-domain={{ oauth_email_domain }} 53 | {% endif %} 54 | {% if oauth_client_id is defined %} 55 | - --client-id={{ oauth_client_id }} 56 | {% endif %} 57 | {% if oauth_client_secret is defined %} 58 | - --client-secret={{ oauth_client_secret }} 59 | {% endif %} 60 | {% endif %} 61 | {% if oauth_access_rule != "" %} 62 | - {{ oauth_access_rule }} 63 | {% endif %} 64 | {% if oauth_extra_opts is defined %} 65 | {% for item in oauth_extra_opts %} 66 | - {{ item }} 67 | {% endfor %} 68 | {% endif %} 69 | image: "{{ oauth_image_fqin }}" 70 | imagePullPolicy: "{{ image_pull_policy }}" 71 | name: oauth-proxy 72 | ports: 73 | - containerPort: 8081 74 | name: public 75 | protocol: TCP 76 | resources: {} 77 | terminationMessagePath: /dev/termination-log 78 | terminationMessagePolicy: File 79 | volumeMounts: 80 | - mountPath: /etc/tls/private 81 | name: {{ ui_tls_secret_name }} 82 | {% endif %} 83 | - name: {{ ui_container_name }} 84 | image: "{{ ui_image_fqin }}" 85 | imagePullPolicy: "{{ image_pull_policy }}" 86 | env: 87 | - name: APP_NAME 88 | value: "{{ app_name }}" 89 | - name: PROFILE 90 | value: "{{ app_profile }}" 91 | - name: VERSION 92 | value: "{{ app_version }}" 93 | - name: UI_INGRESS_PROXY_BODY_SIZE 94 | value: '{{ui_ingress_proxy_body_size}}' 95 | - name: TACKLE_HUB_URL 96 | value: "{{ hub_url }}" 97 | {% if kai_llm_proxy_enabled|bool %} 98 | - name: KAI_LLM_PROXY_URL 99 | value: "{{ kai_llm_proxy_url }}" 100 | {% endif %} 101 | {% if feature_auth_required|bool and feature_auth_type == "keycloak" %} 102 | - name: AUTH_REQUIRED 103 | value: "true" 104 | - name: KEYCLOAK_REALM 105 | value: {{ keycloak_sso_realm }} 106 | - name: KEYCLOAK_CLIENT_ID 107 | value: {{ keycloak_sso_client_id }} 108 | {% if app_profile == 'mta' %} 109 | - name: KEYCLOAK_SERVER_URL 110 | value: {{ rhbk_url }} 111 | {% else %} 112 | - name: KEYCLOAK_SERVER_URL 113 | value: {{ keycloak_sso_url }} 114 | {% endif %} 115 | {% else %} 116 | - name: AUTH_REQUIRED 117 | value: "false" 118 | {% endif %} 119 | - name: NODE_EXTRA_CA_CERTS 120 | value: {{ ui_node_extra_ca_certs }} 121 | {% if ui_tls_enabled|bool %} 122 | - name: UI_TLS_ENABLED 123 | value: 'true' 124 | - name: UI_TLS_CERTIFICATE 125 | value: "/var/run/secrets/{{ ui_tls_secret_name }}/tls.crt" 126 | - name: UI_TLS_KEY 127 | value: "/var/run/secrets/{{ ui_tls_secret_name }}/tls.key" 128 | {% else %} 129 | - name: UI_TLS_ENABLED 130 | value: 'false' 131 | {% endif %} 132 | - name: RWX_SUPPORTED 133 | value: "{{ rwx_supported | string | lower }}" 134 | ports: 135 | - containerPort: {{ ui_port }} 136 | protocol: TCP 137 | resources: 138 | limits: 139 | cpu: {{ ui_container_limits_cpu }} 140 | memory: {{ ui_container_limits_memory }} 141 | requests: 142 | cpu: {{ ui_container_requests_cpu }} 143 | memory: {{ ui_container_requests_memory }} 144 | livenessProbe: 145 | exec: 146 | command: 147 | - /bin/sh 148 | - '-c' 149 | - 'ps -A | grep node' 150 | initialDelaySeconds: 10 151 | timeoutSeconds: 1 152 | periodSeconds: 5 153 | successThreshold: 1 154 | failureThreshold: 3 155 | readinessProbe: 156 | httpGet: 157 | path: / 158 | port: {{ ui_port }} 159 | scheme: {{ ui_proto|upper }} 160 | initialDelaySeconds: 10 161 | timeoutSeconds: 1 162 | periodSeconds: 5 163 | successThreshold: 1 164 | failureThreshold: 3 165 | volumeMounts: 166 | {% if ui_tls_enabled|bool %} 167 | - name: {{ ui_tls_secret_name }} 168 | mountPath: /var/run/secrets/{{ ui_tls_secret_name }}/tls.crt 169 | {% endif %} 170 | {% if trusted_ca_enabled is defined and trusted_ca_enabled|bool %} 171 | - name: trusted-ca 172 | mountPath: /etc/pki/ca-trust/extracted/pem 173 | readOnly: true 174 | {% endif %} 175 | serviceAccount: {{ ui_serviceaccount_name }} 176 | volumes: 177 | {% if ui_tls_enabled|bool or (feature_auth_required|bool and feature_auth_type == "oauth") %} 178 | - name: {{ ui_tls_secret_name }} 179 | secret: 180 | secretName: {{ ui_tls_secret_name }} 181 | defaultMode: 420 182 | {% endif %} 183 | {% if trusted_ca_enabled is defined and trusted_ca_enabled|bool %} 184 | - name: trusted-ca 185 | configMap: 186 | name: trusted-ca 187 | items: 188 | - key: ca-bundle.crt 189 | path: tls-ca-bundle.pem 190 | {% endif %} 191 | -------------------------------------------------------------------------------- /hack/install-tackle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -E 4 | set -e 5 | set -x 6 | 7 | # Figure out where we are being run from. 8 | # This relies on script being run from: 9 | # - ${PROJECT_ROOT}/hack/install-tackle.sh 10 | # - ${PROJECT_ROOT}/bin/install-tackle.sh 11 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 12 | __root="$(cd "$(dirname "${__dir}")" && pwd)" 13 | __repo="$(basename "${__root}")" 14 | __bin_dir="${__root}/bin" 15 | __os="$(uname -s | tr '[:upper:]' '[:lower:]')" 16 | __arch="$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')" 17 | 18 | # Update PATH for execution of this script 19 | export PATH="${__bin_dir}:${PATH}" 20 | 21 | NAMESPACE="${NAMESPACE:-konveyor-tackle}" 22 | OPERATOR_BUNDLE_IMAGE="${OPERATOR_BUNDLE_IMAGE:-quay.io/konveyor/tackle2-operator-bundle:latest}" 23 | HUB_IMAGE="${HUB_IMAGE:-quay.io/konveyor/tackle2-hub:latest}" 24 | UI_IMAGE="${UI_IMAGE:-quay.io/konveyor/tackle2-ui:latest}" 25 | UI_INGRESS_CLASS_NAME="${UI_INGRESS_CLASS_NAME:-nginx}" 26 | ADDON_ANALYZER_IMAGE="${ADDON_ANALYZER_IMAGE:-quay.io/konveyor/tackle2-addon-analyzer:latest}" 27 | JAVA_PROVIDER_IMAGE="${JAVA_PROVIDER_IMAGE:-quay.io/konveyor/java-external-provider:latest}" 28 | C_SHARP_PROVIDER_IMAGE="${C_SHARP_PROVIDER_IMAGE:-quay.io/konveyor/c-sharp-provider:latest}" 29 | IMAGE_PULL_POLICY="${IMAGE_PULL_POLICY:-Always}" 30 | ANALYZER_CONTAINER_REQUESTS_MEMORY="${ANALYZER_CONTAINER_REQUESTS_MEMORY:-0}" 31 | ANALYZER_CONTAINER_REQUESTS_CPU="${ANALYZER_CONTAINER_REQUESTS_CPU:-0}" 32 | FEATURE_AUTH_REQUIRED="${FEATURE_AUTH_REQUIRED:-false}" 33 | TIMEOUT="${TIMEOUT:-15m}" 34 | OLM_VERSION="${OLM_VERSION:-0.28.0}" 35 | DISABLE_MAVEN_SEARCH="${DISABLE_MAVEN_SEARCH:-false}" 36 | 37 | if ! command -v kubectl >/dev/null 2>&1; then 38 | kubectl_bin="${__bin_dir}/kubectl" 39 | mkdir -p "${__bin_dir}" 40 | curl -Lo "${kubectl_bin}" "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/${__os}/${__arch}/kubectl" 41 | chmod +x "${kubectl_bin}" 42 | fi 43 | 44 | if ! command -v operator-sdk1 >/dev/null 2>&1; then 45 | operator_sdk_bin="${__bin_dir}/operator-sdk" 46 | mkdir -p "${__bin_dir}" 47 | 48 | version=$(curl --silent "https://api.github.com/repos/operator-framework/operator-sdk/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') 49 | curl -Lo "${operator_sdk_bin}" "https://github.com/operator-framework/operator-sdk/releases/download/${version}/operator-sdk_${__os}_${__arch}" 50 | chmod +x "${operator_sdk_bin}" 51 | fi 52 | 53 | debug() { 54 | set +e 55 | echo "Install Konveyor FAILED!!!" 56 | echo "What follows is some info that may be useful in debugging the failure" 57 | 58 | if [ "${CI}" == "true" ]; then 59 | debug_output="/tmp/konveyor-debug" 60 | mkdir -p "${debug_output}" 61 | namespace="${debug_output}/namespace.yaml" 62 | all="${debug_output}/all_resources.yaml" 63 | operator="${debug_output}/operator_resources.yaml" 64 | tackle="${debug_output}/tackle.yaml" 65 | pods="${debug_output}/pod_descriptions.yaml" 66 | 67 | kubectl get namespace "${NAMESPACE}" -o yaml | tee "${namespace}" 68 | kubectl get --namespace "${NAMESPACE}" all | tee "${all}" 69 | kubectl get --namespace "${NAMESPACE}" -o yaml \ 70 | subscriptions.operators.coreos.com,catalogsources.operators.coreos.com,installplans.operators.coreos.com,clusterserviceversions.operators.coreos.com | tee "${operator}" 71 | kubectl get --namespace "${NAMESPACE}" -o yaml tackles.tackle.konveyor.io/tackle | tee "${tackle}" 72 | 73 | for pod in $(kubectl get pods -n "${NAMESPACE}" -o jsonpath='{.items[*].metadata.name}'); do 74 | kubectl --namespace "${NAMESPACE}" describe pod "${pod}" | tee -a "${pods}" 75 | kubectl --namespace "${NAMESPACE}" logs "${pod}" | tee "${debug_output}/${pod}.log" 76 | done 77 | else 78 | kubectl get namespace "${NAMESPACE}" -o yaml 79 | kubectl get --namespace "${NAMESPACE}" all 80 | kubectl get --namespace "${NAMESPACE}" -o yaml \ 81 | subscriptions.operators.coreos.com,catalogsources.operators.coreos.com,installplans.operators.coreos.com,clusterserviceversions.operators.coreos.com 82 | kubectl get --namespace "${NAMESPACE}" -o yaml tackles.tackle.konveyor.io/tackle 83 | 84 | for pod in $(kubectl get pods -n "${NAMESPACE}" -o jsonpath='{.items[*].metadata.name}'); do 85 | kubectl --namespace "${NAMESPACE}" describe pod "${pod}" 86 | done 87 | fi 88 | 89 | exit 1 90 | } 91 | trap 'debug' ERR 92 | 93 | install_operator() { 94 | kubectl auth can-i create namespace --all-namespaces 95 | kubectl create namespace ${NAMESPACE} || true 96 | operator-sdk run bundle "${OPERATOR_BUNDLE_IMAGE}" --namespace "${NAMESPACE}" --timeout "${TIMEOUT}" 97 | 98 | # If on MacOS, need to install `brew install coreutils` to get `timeout` 99 | timeout 600s bash -c 'until kubectl get customresourcedefinitions.apiextensions.k8s.io tackles.tackle.konveyor.io; do sleep 30; done' \ 100 | || kubectl get subscription --namespace ${NAMESPACE} -o yaml konveyor-operator # Print subscription details when timed out 101 | } 102 | 103 | kubectl get customresourcedefinitions.apiextensions.k8s.io clusterserviceversions.operators.coreos.com || operator-sdk olm install --version ${OLM_VERSION} 104 | olm_namespace=$(kubectl get clusterserviceversions.operators.coreos.com --all-namespaces | grep packageserver | awk '{print $1}') 105 | kubectl rollout status -w deployment/olm-operator --namespace="${olm_namespace}" 106 | kubectl rollout status -w deployment/catalog-operator --namespace="${olm_namespace}" 107 | kubectl wait --namespace "${olm_namespace}" --for='jsonpath={.status.phase}'=Succeeded clusterserviceversions.operators.coreos.com packageserver 108 | kubectl get customresourcedefinitions.apiextensions.k8s.io tackles.tackle.konveyor.io || install_operator 109 | 110 | 111 | # Create, and wait for, tackle 112 | kubectl wait \ 113 | --namespace ${NAMESPACE} \ 114 | --for=condition=established \ 115 | customresourcedefinitions.apiextensions.k8s.io/tackles.tackle.konveyor.io 116 | cat < -obic 74 | ``` 75 | 76 | ## Build and push a development operator container image 77 | 78 | This example only builds and pushes an operator image, if changes were only made to the ansible roles, this is the option that makes the most sense. 79 | 80 | ``` 81 | ./tackle-opdev.sh -n -o 82 | ``` 83 | 84 | ## Build and push a development operator bundle and index image 85 | 86 | This is useful when only OLM metadata changes have taken place, bundle and index images are built and also a CatalogSource is created. In addition, the ClusterServiceVersion (CSV) is modified to use the custom operator development image prior building the bundle. 87 | 88 | ``` 89 | ./tackle-opdev.sh -n -bic 90 | ``` 91 | 92 | ## Testing a development operator using a deployment 93 | 94 | Before continuing, ensure the existance and health of the CatalogSource: 95 | 96 | ``` 97 | kubectl -n konveyor-tackle get catalogsource 98 | NAME DISPLAY TYPE PUBLISHER AGE 99 | konveyor-tackle Tackle Development grpc Konveyor 6m22s 100 | ``` 101 | 102 | The index image used by _CatalogSource_ includes the bundle created for this development build. 103 | 104 | ### Deploy a development operator 105 | 106 | The deploy option will create a few resources necessary to install Tackle using OLM such as ensuring an _OperatorGroup_ and _Subscription_ are in place (by default all objects are created in the konveyor-tackle namespace). 107 | 108 | ``` 109 | ./tackle-opdev.sh -n -d 110 | ``` 111 | 112 | ### Check operator health: 113 | 114 | ``` 115 | kubectl -n konveyor-tackle get pods 116 | 117 | NAME READY STATUS RESTARTS AGE 118 | 9b6988c5dc0b97094709d01690545526798779eaa2285b4e9f67620573ldd7k 0/1 Completed 0 69s 119 | konveyor-tackle-jw4xr 1/1 Running 0 10m 120 | tackle-operator-687cb57d7d-tsb8v 1/1 Running 0 56s 121 | ``` 122 | 123 | The operator pod can be inspected further by ensuring the correct image is being pulled and there are other warnings or errors in logs. 124 | 125 | ### Create a _Tackle_ CR 126 | 127 | Create and customize the CR spec as needed, example: 128 | 129 | ``` 130 | $ cat << EOF | kubectl -n konveyor-tackle apply -f - 131 | kind: Tackle 132 | apiVersion: tackle.konveyor.io/v1alpha1 133 | metadata: 134 | name: tackle 135 | namespace: konveyor-tackle 136 | spec: 137 | EOF 138 | ``` 139 | 140 | ### Check status of _Tackle_ CR 141 | 142 | The operator watches _Tackle_ and manages the status, we can check the health of each reconcile cycle by inspecting the CR: 143 | 144 | ``` 145 | kubectl describe tackle 146 | ... 147 | Status: 148 | Conditions: 149 | Last Transition Time: 2022-07-19T13:53:54Z 150 | Message: 151 | Reason: 152 | Status: False 153 | Type: Failure 154 | Ansible Result: 155 | Changed: 0 156 | Completion: 2022-07-19T13:56:22.050148 157 | Failures: 0 158 | Ok: 33 159 | Skipped: 17 160 | Last Transition Time: 2022-07-19T13:51:44Z 161 | Message: Awaiting next reconciliation 162 | Reason: Successful 163 | Status: True 164 | Type: Running 165 | Last Transition Time: 2022-07-19T13:56:22Z 166 | Message: Last reconciliation succeeded 167 | Reason: Successful 168 | Status: True 169 | Type: Successful 170 | Events: 171 | ``` 172 | 173 | Any operator errors encountered during reconcile will be reported, for further info, the operator pod logs can be inspected. 174 | 175 | ## Cleanup 176 | 177 | Use the tackle [cleanup script](../tools/tackle-cleanup.sh) which is supplied with operator, it will ensure all resources are properly deleted. 178 | 179 | Example for k8s clusters: 180 | 181 | ``` 182 | ./tackle-cleanup.sh -k 183 | ``` 184 | -------------------------------------------------------------------------------- /docs/oadp-backups.md: -------------------------------------------------------------------------------- 1 | # Backing up with Konveyor with OADP 2 | 3 | ## Introduction 4 | The purpose of this document is to demonstrate a possible backup strategy for obtaining quiesced and consistent backups of Konveyor. 5 | 6 | Backing up Konveyor with OADP is relatively straight forward, although it does require some planning. 7 | 8 | In order to quiesce databases and ensure consistent backups the easiest approach is to scale down the pods that make up the Konveyor application. We can then use datamover to backup the PVCs in addition to the Kubernetes and OpenShift resources that make up Konveyor. Unfortunately datamover currently requires a storageclass with the `Immediate` volume binding mode if we want to backup PVCs for an application while the pods are scaled down. This requirement should be alleviated in a future release of OADP, but for now the practical affect of this is that we must restrict Konveyor to running pods in a single availability zone. If we try to use the immediate volume binding mode do not take care pods can be launched on a node in a different availability zone than the PVC and will fail to start. 9 | 10 | ## Install Konveyor 11 | 12 | - Prepare a single AZ StorageClass 13 | The process will vary depending on your storage provider. For AWS this can be achieved with the EBS CSI driver, by cloning the gp3-csi storageclass, specifying the allowed topologies, and updating the volumeBinding Mode. 14 | ``` 15 | cat << EOF | oc create -f - 16 | allowVolumeExpansion: true 17 | allowedTopologies: 18 | - matchLabelExpressions: 19 | - key: topology.kubernetes.io/zone 20 | values: 21 | - us-west-2a 22 | apiVersion: storage.k8s.io/v1 23 | kind: StorageClass 24 | metadata: 25 | name: gp3-csi-konveyor 26 | parameters: 27 | encrypted: "true" 28 | type: gp3 29 | provisioner: ebs.csi.aws.com 30 | reclaimPolicy: Delete 31 | volumeBindingMode: Immediate 32 | EOF 33 | ``` 34 | - Install the Konveyor Operator 35 | - Annotate the namespace with a node selector 36 | `oc annotate namespace konveyor-tackle openshift.io/node-selector1=topology.kubernetes.io/zone=us-west-2a` 37 | - Create a tackle CR that makes use of the new storage class 38 | ``` 39 | cat << EOF | oc create -f - 40 | apiVersion: tackle.konveyor.io/v1alpha1 41 | kind: Tackle 42 | metadata: 43 | name: tackle 44 | namespace: konveyor-tackle 45 | spec: 46 | cache_storage_class: gp3-csi-konveyor 47 | feature_auth_required: "true" 48 | hub_bucket_storage_class: gp3-csi-konveyor 49 | rwo_storage_class: gp3-csi-konveyor 50 | EOF 51 | ``` 52 | 53 | After a few moments we should have a working install of Konveyor that is pinned to a single availability zone. 54 | 55 | ## Install OpenShift OADP 56 | - Install the OADP Operator from OperatorHub 57 | - Create a cloud secret with the credentials for your backup location. 58 | The format of this file will vary. For AWS and other S3 storage options it will take the form of a standard aws credential file. 59 | ``` 60 | [default] 61 | aws_access_key_id = 62 | aws_secret_access_key = 63 | ``` 64 | Once the file is created use it to generate the secret. 65 | `oc create secret generic cloud-credentials --namespace openshift-adp --from-file cloud=cloud-credentials` 66 | - Create a dpa resource with data mover enabled 67 | ``` 68 | cat << EOF | oc create -f - 69 | apiVersion: oadp.openshift.io/v1alpha1 70 | kind: DataProtectionApplication 71 | metadata: 72 | name: velero 73 | namespace: openshift-adp 74 | spec: 75 | backupLocations: 76 | - velero: 77 | config: 78 | profile: default 79 | region: us-west-2 80 | credential: 81 | key: cloud 82 | name: cloud-credentials 83 | default: true 84 | objectStorage: 85 | bucket: konveyor-backups 86 | prefix: velero 87 | provider: aws 88 | configuration: 89 | nodeAgent: 90 | enable: true 91 | uploaderType: kopia 92 | velero: 93 | defaultPlugins: 94 | - openshift 95 | - aws 96 | - kubevirt 97 | - csi 98 | defaultSnapshotMoveData: true 99 | defaultVolumesToFSBackup: false 100 | featureFlags: 101 | - EnableCSI 102 | EOF 103 | ``` 104 | 105 | ## Running Backups 106 | We must take care to shut down the pods, run the backup, and bring the pods back up. Once possible way to achieve this is to run a cronjob that performs these actions. 107 | 108 | - The CronJob will run with a serviceaccount. In the example below I named it job-runner. 109 | ``` 110 | oc create sa job-runner 111 | ``` 112 | 113 | - Grant OADP NS Permissions 114 | The job-runner SA will need to be able to create and monitor resources in the openshift-adp namespace so we give it permission to do so. 115 | ``` 116 | oc project openshift-adp 117 | oc adm policy add-role-to-user admin -z job-runner 118 | ``` 119 | 120 | - Grant Konveyor NS Permissions 121 | The job-runner SA will also need to shutdown, start, and monitor resources in the konveyor-tackle namespace so, once again give it permission to do so 122 | ``` 123 | oc project konveyor-tackle 124 | oc adm policy add-role-to-user admin system:serviceaccount:openshift-adp:job-runner 125 | ``` 126 | 127 | - Create a CronJob 128 | Note that the time is cluster time, which by default is UTC. 129 | 130 | The RHSSO operator will fail to reconcile if we restore either the `prometheusrules.monitoring.coreos.com` or `servicemonitors.monitoring.coreos.com` resource due to missing ownerRefs. In turn the Konveyor Operator will also fail to reconcile. We can avoid this problem by excluding the resources from the backup and allowing the RHSSO operator to recreate them. 131 | 132 | ``` 133 | apiVersion: batch/v1 134 | kind: CronJob 135 | metadata: 136 | name: konveyor-backup 137 | namespace: openshift-adp 138 | spec: 139 | schedule: "30 16 * * 1-5" 140 | concurrencyPolicy: Allow 141 | successfulJobsHistoryLimit: 1 142 | failedJobsHistoryLimit: 1 143 | jobTemplate: 144 | spec: 145 | template: 146 | spec: 147 | containers: 148 | - name: konveyor-backup 149 | image: registry.redhat.io/openshift4/ose-cli:latest 150 | command: 151 | - bash 152 | - -c 153 | - | 154 | oc scale -n konveyor-tackle deployment tackle-operator --replicas=0 155 | oc wait -n konveyor-tackle po -l name=tackle-operator --for=delete 156 | oc scale -n konveyor-tackle deployment tackle-keycloak-sso --replicas=0 157 | oc wait -n konveyor-tackle po -l app.kubernetes.io/name=tackle-keycloak-sso --for=delete 158 | oc scale -n konveyor-tackle deployment tackle-keycloak-postgresql-15 --replicas=0 159 | oc wait -n konveyor-tackle po -l app.kubernetes.io/name=tackle-keycloak-postgresql-15 --for=delete 160 | oc scale -n konveyor-tackle deployment tackle-ui --replicas=0 161 | oc wait -n konveyor-tackle po -l app.kubernetes.io/name=tackle-ui --for=delete 162 | oc scale -n konveyor-tackle deployment tackle-hub --replicas=0 163 | oc wait -n konveyor-tackle po -l app.kubernetes.io/name=tackle-hub --for=delete 164 | export BACKUP_DATE=$(date +%Y-%m-%d-%H-%M-%S) 165 | cat << EOF | oc apply -f - 166 | kind: Backup 167 | apiVersion: velero.io/v1 168 | metadata: 169 | name: konveyor-backup-$BACKUP_DATE 170 | namespace: openshift-adp 171 | spec: 172 | excludedResources: 173 | - prometheusrules.monitoring.coreos.com 174 | - servicemonitors.monitoring.coreos.com 175 | csiSnapshotTimeout: 30m0s 176 | defaultVolumesToFsBackup: false 177 | includedNamespaces: 178 | - konveyor-tackle 179 | itemOperationTimeout: 30h0m0s 180 | snapshotMoveData: true 181 | storageLocation: velero-1 182 | ttl: 720h0m0s 183 | EOF 184 | while [[ $(oc get -n openshift-adp backup konveyor-backup-$BACKUP_DATE -o go-template='{{ .status.phase }}') != "Completed" ]]; do sleep 5; done 185 | oc scale -n konveyor-tackle statefulsets --all --replicas=1 186 | oc scale -n konveyor-tackle deployments --all --replicas=1 187 | restartPolicy: OnFailure 188 | serviceAccount: job-runner 189 | ``` 190 | 191 | ## Running Restores 192 | 193 | Running a restore of the namespace is not much different than a restore for any other application. All we need to do is pick a backup to restore and create the restore resource: 194 | 195 | ``` 196 | apiVersion: velero.io/v1 197 | kind: Restore 198 | metadata: 199 | name: restore 200 | namespace: openshift-adp 201 | spec: 202 | backupName: backup-2025-03-12-14-25-24 203 | includedResources: [] 204 | excludedResources: 205 | - nodes 206 | - events 207 | - events.events.k8s.io 208 | - backups.velero.io 209 | - restores.velero.io 210 | - resticrepositories.velero.io 211 | restorePVs: true 212 | ``` 213 | -------------------------------------------------------------------------------- /helm/templates/olm/konveyor-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.olm }} 2 | apiVersion: operators.coreos.com/v1alpha1 3 | kind: ClusterServiceVersion 4 | metadata: 5 | annotations: 6 | alm-examples: '[]' 7 | capabilities: Seamless Upgrades 8 | categories: Modernization & Migration 9 | certified: "false" 10 | containerImage: {{ .Values.images.operator }} 11 | description: Konveyor is an open-source application modernization platform that 12 | helps organizations safely and predictably modernize applications to Kubernetes 13 | at scale. 14 | features.operators.openshift.io/disconnected: "true" 15 | features.operators.openshift.io/fips-compliant: "false" 16 | features.operators.openshift.io/proxy-aware: "true" 17 | features.operators.openshift.io/cnf: "false" 18 | features.operators.openshift.io/cni: "false" 19 | features.operators.openshift.io/csi: "false" 20 | features.operators.openshift.io/tls-profiles: "false" 21 | features.operators.openshift.io/token-auth-aws: "false" 22 | features.operators.openshift.io/token-auth-azure: "false" 23 | features.operators.openshift.io/token-auth-gcp: "false" 24 | olm.skipRange: '>=0.0.0 <{{ .Values.version }}' 25 | operatorframework.io/initialization-resource: |- 26 | { 27 | "apiVersion": "tackle.konveyor.io/v1alpha1", 28 | "kind": "Tackle", 29 | "metadata": { 30 | "name": "tackle", 31 | "namespace": "konveyor-tackle" 32 | }, 33 | "spec": { 34 | "feature_auth_required": "false" 35 | } 36 | } 37 | operatorframework.io/suggested-namespace: konveyor-tackle 38 | repository: https://github.com/konveyor/tackle2-operator 39 | support: https://github.com/konveyor/tackle2-operator/issues 40 | labels: 41 | operatorframework.io/arch.amd64: supported 42 | operatorframework.io/arch.arm64: supported 43 | operatorframework.io/arch.ppc64le: unsupported 44 | operatorframework.io/arch.s390x: unsupported 45 | name: konveyor-operator.v{{ .Values.version }} 46 | namespace: konveyor-tackle 47 | spec: 48 | apiservicedefinitions: {} 49 | customresourcedefinitions: 50 | owned: 51 | - description: Tackle Addon 52 | displayName: Addon 53 | kind: Addon 54 | name: addons.tackle.konveyor.io 55 | version: v1alpha1 56 | - description: Tackle Extension 57 | displayName: Extension 58 | kind: Extension 59 | name: extensions.tackle.konveyor.io 60 | version: v1alpha1 61 | - description: Tackle 62 | displayName: Tackle 63 | kind: Tackle 64 | name: tackles.tackle.konveyor.io 65 | version: v1alpha1 66 | - description: Tackle Task 67 | displayName: Task 68 | kind: Task 69 | name: tasks.tackle.konveyor.io 70 | version: v1alpha1 71 | description: | 72 | Konveyor is an open-source application modernization platform that helps organizations safely and predictably modernize applications to new technologies, with an initial focus on accelerating the adoption of legacy applications to Kubernetes. 73 | 74 | See the [Konveyor Unified Experience](https://github.com/konveyor/enhancements/tree/master/enhancements/unified_experience) to understand the vision of the project and the [Konveyor Charter](https://github.com/konveyor/community/blob/main/Charter.md) for more information on the community. 75 | 76 | ### Install 77 | 78 | Once you have successfully installed the Operator, proceed to deploy components by creating the required Tackle CR. 79 | 80 | By default, the Operator installs the following components on a target cluster: 81 | 82 | * Hub, to manage the application inventory and coordinate the migration process. 83 | * UI, the web console to manage the application inventory and drive the migration waves. 84 | 85 | Authentication capabilities may be enabled via the `feature_auth_required` parameter in the Tackle CR. When enabled, the Operator installs the following components: 86 | * Keycloak, to manage authentication, including with 3rd-party providers. 87 | 88 | ### Examples 89 | If you are just getting started using Konveyor, take a look at an [example application analysis here](https://github.com/konveyor/example-applications/tree/main/example-1) 90 | 91 | ### Documentation 92 | Documentation can be found on our [website](https://konveyor.github.io/tackle). 93 | 94 | ### Getting help 95 | If you encounter any issues while using Konveyor operator, you can create an issue on our [Github repo](https://github.com/konveyor/tackle2-operator/issues), for bugs, enhancements or other requests. 96 | Or reach out to us in [#konveyor](https://kubernetes.slack.com/archives/CR85S82A2) on kubernetes.slack.com 97 | 98 | ### Contributing 99 | You can contribute by: 100 | 101 | * Raising any issues you find using Konveyor Operator 102 | * Fixing issues by opening [Pull Requests](https://github.com/konveyor/tackle2-operator/pulls) 103 | * Improving [documentation](https://github.com/konveyor/tackle-documentation) 104 | displayName: Konveyor Operator 105 | icon: 106 | - base64data: iVBORw0KGgoAAAANSUhEUgAAAKsAAACrCAYAAAAZ6GwZAAAACXBIWXMAAAsSAAALEgHS3X78AAALWUlEQVR4nO2dPWwcxxXHR0GgQBICMhAQwI4BXgqnsJOINnZrnly4iALwAgJGUokswgBsRFfpTtSmSiW6MA2zEdkKJnwCLBcpomN9Z+cYWy7swneAYReBYx0QxYibC+bwTh4d583H3pJ7j/f/AQuCvP2YmfefN2/ezB7PDQYDBYAEfgArASlArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjH8cBZMdbCzPq+UWjylxz1a2djtnNKzZooz/1oLCbWplLoyBcXJw9rKxu6evGIXzyyEAZuChaq5MwVlmAoQswIxQKxADBArEAPECsQwE6mrIvjks6+i71J57rK6eOH8NFZHJBBrAHff/1Ddvf9B9HWXLpxXb/3lDxBsQSAMCODhp1/muu7xt9+p7hdfn3Jpzy4QawC/feVXua7TYcALzz9zyqU9uyAMCCD99YJ6580/Tn05zzrwrEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjHMglgbSqn+FJQjL/syi1085waDwVmr0zEOdtYr+j/9TFmxgljZ2G0KKOapMBNiBWcDxKxADKX907aDnfV5pdSmUKl0VjZ2G+N/PNhZX1RK1cop0sQ0VjZ2O9NcwNLCgIOddW3s5VIeXgy/MwVLcbE29pzQ+uhJ6OLKxm53CspipcwwYL7EZxfB4tg9KoKFqqjsUz0JRcwKxACxAjFArEAMECsQQ2mpqyJo/bOn3vv7R1F3unTxR+q1ay+rys8uT2Wd7r7/oXr46ZdR17z4i2fVa795+cTKNC2IFeu//v0f9de3/5br2u4XX6ud7PeFl2lS7j/4WN29/0H0XR5+9pW6dOG8unb1l9NWpUIRGwZo4+gjDxdzXgfKRaxn1YL7859eVe89+Fg9/u//gq/76eUfq2uvTKcH0p7x8bffRYcBP3/u8pn3qkp6zPrC888Mj7PEMPacgfgzD8gGADFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxACxAjFArEAMZYp1CTIBMcCzAjFArEAMECsQA8QKxACxAjFArEAMECsQA8QKxFCmWPuQCYihTLFWBQv2SCm1bf5hZWO3qZTaL69IE7NPdZhazg0GA7nNC2YKxKxADBArEAPECsQAsQIxQKxADBArEAPECsRw6v8VO0mzeaXUolKqQkdHKfWo3apPdUJ6lkjSTNtn3qiytk+n7CY4lUUBEugqHVccp95TSjXarfreiRcKPEWSZjWyj15ZnHPYZ6/dqjfKaL0nYk3SbMt2QrtVt/49SbPNsd5nst1u1R8Z5205GsCGXs7c5LxtkmZValSTjqsRkzSrkDHG6erOkaTZKnl66+eO+9rKMkS3nevzAJrUxtrT2XjSzpZy1bjrTJtS+bY9TmScnm7LSPtwdHU9261613eiGQbcZM45JtYkzXTlbjDn39INSN5UG3k5sNAmuuEeJGn2ertV37Z8XrWVN0mzlxzDVYWp4yGVc5GpU58+59Aiv2757IjazlrWCJqO67u2shltb3MQh8Z5WznLtkD2ucU4s+g6J2mmy1XjOp/KM8EiD8QJdZ+8yTw1ch6hmtymjhFKk56dB06QcxTDcdSYv8eUm4W81xOBjbHpKBM3kg1HlyTN9ibsRJqbdJ8i0K/md8krW4kSKxmNM8KR0Xh7kcOKixvUQUKYo04SDXnkI+Y66/OpPThRFBnXWUMx3cZMR+JErJ1Jl0Iz22iQh+tcCJmDOfLYtnAsXKzksRqMcfojF07C8nnUHgXrb5DX8G0V3OYqYOFKpDc24bwE19u5TnTPNZzFEuNdyTNxjmKLxH07oAiHxuHjpmf0icVqhxjP2qBYxUaNeuy8Z/jTnutqu1WvtFt1fY2eRFXbrbq+7nWHaOcc3sVGjDc24cR6heksnIhPIpvB1f/6WOjD1XufJjEu+/TJDj8huwwP/btSas3jVAoJe4glWzgQlGclT8V93c+aMSt0xUr7NMO3ehw9kUrSrEnDuO0ew+EmZNZI6Pt1YvKDNDLcY0aGqilCEq/Ng/UDUzvc5IQrW5MmITY7rBqjDze8j7wqZ8cjmuEfay+ymc6YNMg+tnoPBebJlx+S+IeQILnRujoe0nk9a8CEyvQiXK/uuYQ6ghrKlfLgJjM2dAM0cky4OK84/myuLCeZg+TEPQoFfF6V+7zPCdWE7FdzeNio0YyEzbXXsbAixLOyE6p2qz5eOK7XeoU6QjdYkmb7jIeoRQ43CyS+YJFrr5ikWd/S28e9LdepQstX9UxM9sZHEYd3XSAvZZtY9Q2Rs2UOHYEo3NtmMgl58smcLo45mRCxchOqpwrmSDmEDosme4xY8wTxyySKmCxBw/Z8nWg36mILFXoRYceS55sUm5RHHUfX5QFTZputtg3RcxOv2BibS3txcxorNOpxjuSYiPNuZOlEzHaj15QdcU/MKpjJzcgQgvOOw3vQ6lDMdYVBbXPPcj/OqWyr72NsKxHzgCF0fs/2mStPqp2NnpeMDuqMnMCPaSCvWJcoVycJLu4+BnlHmzFGhigjXjUJbXtzOZYTK5db9hElcGLOGFGWHM7Humo4yRbB24G5tegVpYic6jiHEzT+ODYvuUB1tnmPw1gPlRd6ju+1775ZB8dolXfxhrN9Efll656HSfezmurnDMXlKF1wQ0mIEIv6PgLOS24yQ1ds3Kc71i3H4RO+L+3FbnIZxzN0285nV+4K2Eq4z6X0QvezcrnH4WoRJfe7zCxakYFjwgbOEN6GoFypbvx/RDzPdp8uM/O2Tfz6OUKAZkyelSkflzXpMyMDl6fdjJyAcrYsYlRjdRLiWXXSv+ZY7rth9EzOYDdCey+lRbigO0gQ1LvXQs71EOotG0Uur0bAeV9uAsy133Loih9NLrmFB599ejSSuEIYVqxez2ok/VfJs9k8556xyYWtiG4Qz55T19bDXkwKjPaoVifcsNGgOvmyEHkmVpWQDlzwGxQNx76AO0maKc/e3VVPxsPXubuj0YTqbnNKw51ctvg/OGali7lha4GS2B0mraLI4O9S2mJ1FMfqQuvMQpJmXc+MPXrIpEWL3EMTeSevt8i5c/465Ut9R2GQDd9w3O8O2ac2WvnTP+l33WnuuJbTIyeYLk9u7RBREyzaCM2FA8s0RGx6JjhLVOnPkzQbkKFuexLKhxO86jLphMv33FJe8ZiALS5HSmj7vKuU+obs8w397lrA6EfOSXw7yZZto06ebIBrbdgMGYqiH5nQfwryjrkFS43qMu5JLgS4npuLgPX9PDh3+Dtw6WTyPCsVinvInPFCWRETnOGy7qSTFwpPJlnE4Lzn0QnnVk/k3kZ7FCHYtbxxtScsWRhfeMqVZyUxcrHpEm3l0z3j6gTeQQ8RlaJeAabyuOI1F5z3FPsWLrVHdQL76OteKuBN5C1Hp9kyd82d+KvY9LBRnjVkbV83wtYsvo5dxvv6OewzyuG63qytMMu71vpY6m3yJA13al8mbOywqVJFRgF7j4a7DuUr8WUXJWDYZ/QKtznh7Y3sU2JOGd98DeSA77oCYoBYgRggViAGiBWIAWIFYoBYgRggViAGiBWIAWIFYoBYgRggViAGiBWIAWIFYoBYgRggViAGiBWIAWIFMlBK/R87h9mrJ0MSUgAAAABJRU5ErkJggg== 107 | mediatype: image/png 108 | install: 109 | spec: 110 | deployments: [] 111 | strategy: deployment 112 | installModes: 113 | - supported: true 114 | type: OwnNamespace 115 | - supported: true 116 | type: SingleNamespace 117 | - supported: true 118 | type: MultiNamespace 119 | - supported: false 120 | type: AllNamespaces 121 | keywords: 122 | - modernization 123 | - migration 124 | - konveyor 125 | - tackle 126 | links: 127 | - name: Documentation 128 | url: https://konveyor.github.io/tackle 129 | - name: Operator 130 | url: https://github.com/konveyor/tackle2-operator 131 | maintainers: 132 | - email: konveyor-dev@googlegroups.com 133 | name: Konveyor Community 134 | maturity: alpha 135 | minKubeVersion: 1.22.0 136 | provider: 137 | name: Konveyor 138 | url: https://www.konveyor.io 139 | version: {{ .Values.version }} 140 | {{ if .Values.csv.replaces }} 141 | replaces: {{ .Values.csv.replaces }} 142 | {{ end }} 143 | {{ if .Values.csv.skips }} 144 | skips: 145 | {{ range .Values.csv.skips }} 146 | - {{ . }} 147 | {{ end }} 148 | {{ end }} 149 | {{ end }} 150 | -------------------------------------------------------------------------------- /roles/tackle/templates/deployment-hub.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: {{ hub_deployment_name }} 6 | namespace: {{ app_namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ hub_service_name }} 9 | app.kubernetes.io/component: {{ hub_component_name }} 10 | app.kubernetes.io/part-of: {{ app_name }} 11 | annotations: 12 | app.openshift.io/connects-to: >- 13 | [ 14 | {% if feature_auth_required|bool and feature_auth_type == "keycloak" %} 15 | {% if app_profile == 'konveyor' %} 16 | { "apiVersion": "apps/v1", "kind": "Deployment", "name": "{{ keycloak_sso_deployment_name }}" }, 17 | {% elif app_profile == 'mta' %} 18 | { "apiVersion": "apps/v1", "kind": "StatefulSet", "name": "keycloak" }, 19 | {% endif %} 20 | {% endif %} 21 | ] 22 | spec: 23 | replicas: {{ hub_deployment_replicas }} 24 | selector: 25 | matchLabels: 26 | app.kubernetes.io/name: {{ hub_service_name }} 27 | app.kubernetes.io/component: {{ hub_component_name }} 28 | app.kubernetes.io/part-of: {{ app_name }} 29 | {% if hub_deployment_strategy == 'Recreate' %} 30 | strategy: 31 | type: {{ hub_deployment_strategy }} 32 | {% endif %} 33 | template: 34 | metadata: 35 | labels: 36 | app.kubernetes.io/name: {{ hub_service_name }} 37 | app.kubernetes.io/component: {{ hub_component_name }} 38 | app.kubernetes.io/part-of: {{ app_name }} 39 | app: {{ app_name }} 40 | role: {{ hub_service_name }} 41 | {% if feature_auth_required|bool %} 42 | {% if app_profile == 'mta' and feature_auth_type == "keycloak" %} 43 | keycloak_db_secret_name: {{ keycloak_db_secret.env | k8s_config_resource_name }} 44 | {% endif %} 45 | {% endif %} 46 | spec: 47 | serviceAccountName: {{ hub_serviceaccount_name }} 48 | containers: 49 | - name: {{ hub_container_name }} 50 | image: "{{ hub_image_fqin }}" 51 | imagePullPolicy: "{{ image_pull_policy }}" 52 | env: 53 | - name: NAMESPACE 54 | valueFrom: 55 | fieldRef: 56 | apiVersion: v1 57 | fieldPath: metadata.namespace 58 | - name: APP_NAME 59 | value: "{{ app_name }}" 60 | - name: PROFILE 61 | value: "{{ app_profile }}" 62 | - name: VERSION 63 | value: "{{ app_version }}" 64 | - name: HUB_BASE_URL 65 | value: "{{ hub_url }}" 66 | {% if hub_tls_enabled|bool %} 67 | - name: HUB_TLS_ENABLED 68 | value: 'true' 69 | - name: HUB_TLS_CERTIFICATE 70 | value: "/var/run/secrets/{{ hub_tls_secret_name }}/tls.crt" 71 | - name: HUB_TLS_KEY 72 | value: "/var/run/secrets/{{ hub_tls_secret_name }}/tls.key" 73 | {% else %} 74 | - name: HUB_TLS_ENABLED 75 | value: 'false' 76 | {% endif %} 77 | - name: ADDON_SECRET_PATH 78 | value: "/var/run/secrets/{{ app_name }}-addon" 79 | - name: ADDON_WORKING_DIR 80 | value: "{{ hub_addon_working_path }}" 81 | - name: DB_PATH 82 | value: "{{ hub_database_volume_path }}/{{ hub_database_filename }}" 83 | - name: BUCKET_PATH 84 | value: "{{ hub_bucket_volume_path }}" 85 | - name: BUCKET_PVC 86 | value: "{{ hub_bucket_volume_claim_name }}" 87 | - name: ENCRYPTION_PASSPHRASE 88 | valueFrom: 89 | secretKeyRef: 90 | name: "{{ hub_secret_name }}" 91 | key: passphrase 92 | - name: ADDON_TOKEN 93 | valueFrom: 94 | secretKeyRef: 95 | name: "{{ hub_secret_name }}" 96 | key: addon_token 97 | - name: METRICS_ENABLED 98 | value: "{{ hub_metrics_enabled }}" 99 | - name: METRICS_PORT 100 | value: "{{ hub_metrics_port }}" 101 | {% if hub_log_level_master is defined %} 102 | - name: LOG_MASTER 103 | value: "{{ hub_log_level_master }}" 104 | {% endif %} 105 | {% if hub_log_level_migration is defined %} 106 | - name: LOG_MIGRATION 107 | value: "{{ hub_log_level_migration }}" 108 | {% endif %} 109 | {% if hub_log_level_web is defined %} 110 | - name: LOG_WEB 111 | value: "{{ hub_log_level_web }}" 112 | {% endif %} 113 | {% if hub_log_level_reaper is defined %} 114 | - name: LOG_REAPER 115 | value: "{{ hub_log_level_reaper }}" 116 | {% endif %} 117 | {% if hub_log_level_task is defined %} 118 | - name: LOG_TASK 119 | value: "{{ hub_log_level_task }}" 120 | {% endif %} 121 | {% if feature_auth_required|bool and feature_auth_type == "keycloak" %} 122 | - name: AUTH_REQUIRED 123 | value: "true" 124 | {% else %} 125 | - name: AUTH_REQUIRED 126 | value: "false" 127 | {% endif %} 128 | {% if feature_auth_required|bool and feature_auth_type == "keycloak" %} 129 | - name: KEYCLOAK_REALM 130 | value: "{{ keycloak_sso_realm }}" 131 | - name: KEYCLOAK_CLIENT_ID 132 | value: "{{ keycloak_sso_client_id }}" 133 | {% if app_profile == 'mta' %} 134 | - name: KEYCLOAK_HOST 135 | value: "{{ rhbk_url }}" 136 | {% else %} 137 | - name: KEYCLOAK_HOST 138 | value: "{{ keycloak_sso_url }}" 139 | {% endif %} 140 | - name: KEYCLOAK_ADMIN_USER 141 | valueFrom: 142 | secretKeyRef: 143 | name: "{{ keycloak_sso_secret_name }}" 144 | key: username 145 | - name: KEYCLOAK_ADMIN_PASS 146 | valueFrom: 147 | secretKeyRef: 148 | name: "{{ keycloak_sso_secret_name }}" 149 | key: password 150 | - name: KEYCLOAK_REQ_PASS_UPDATE 151 | value: "{{ keycloak_sso_req_passwd_update|lower }}" 152 | - name: KEYCLOAK_AUDIENCE 153 | value: "{{ keycloak_api_audience }}" 154 | {% endif %} 155 | - name: TASK_SA 156 | value: "{{ hub_task_sa }}" 157 | {% if hub_task_reap_created is defined %} 158 | - name: TASK_REAP_CREATED 159 | value: "{{ hub_task_reap_created }}" 160 | {% endif %} 161 | {% if hub_task_reap_succeeded is defined %} 162 | - name: TASK_REAP_SUCCEEDED 163 | value: "{{ hub_task_reap_succeeded }}" 164 | {% endif %} 165 | {% if hub_task_reap_failed is defined %} 166 | - name: TASK_REAP_FAILED 167 | value: "{{ hub_task_reap_failed }}" 168 | {% endif %} 169 | {% if hub_task_retries is defined %} 170 | - name: TASK_RETRIES 171 | value: "{{ hub_task_retries }}" 172 | {% endif %} 173 | - name: RWX_SUPPORTED 174 | value: "{{ rwx_supported | string | lower }}" 175 | {% if rwx_supported|bool %} 176 | - name: CACHE_PVC 177 | value: "{{ cache_data_volume_claim_name }}" 178 | - name: CACHE_PATH 179 | value: "{{ cache_mount_path }}" 180 | {% endif %} 181 | {% if http_proxy|length >0 %} 182 | - name: HTTP_PROXY 183 | value: {{ http_proxy }} 184 | {% endif %} 185 | {% if https_proxy|length >0 %} 186 | - name: HTTPS_PROXY 187 | value: {{ https_proxy }} 188 | {% endif %} 189 | {% if no_proxy|length >0 %} 190 | - name: NO_PROXY 191 | value: {{ no_proxy }} 192 | {% endif %} 193 | {% if feature_analysis_archiver|bool %} 194 | - name: ANALYSIS_ARCHIVER_ENABLED 195 | value: "true" 196 | {% else %} 197 | - name: ANALYSIS_ARCHIVER_ENABLED 198 | value: "false" 199 | {% endif %} 200 | {% if feature_discovery|bool %} 201 | - name: DISCOVERY_ENABLED 202 | value: "true" 203 | {% else %} 204 | - name: DISCOVERY_ENABLED 205 | value: "false" 206 | {% endif %} 207 | - name: KAI_URL 208 | value: "{{ kai_url }}" 209 | ports: 210 | - containerPort: {{ hub_port }} 211 | protocol: TCP 212 | resources: 213 | limits: 214 | cpu: {{ hub_container_limits_cpu }} 215 | memory: {{ hub_container_limits_memory }} 216 | requests: 217 | cpu: {{ hub_container_requests_cpu }} 218 | memory: {{ hub_container_requests_memory }} 219 | securityContext: 220 | runAsUser: {{ hub_uid }} 221 | # TODO: Add liveness and readiness probes 222 | volumeMounts: 223 | - name: {{ hub_database_volume_name }} 224 | mountPath: {{ hub_database_volume_path }} 225 | - name: {{ hub_bucket_volume_name }} 226 | mountPath: {{ hub_bucket_volume_path }} 227 | {% if rwx_supported|bool %} 228 | - name: {{ cache_data_volume_name }} 229 | mountPath: {{ cache_mount_path }} 230 | {% endif %} 231 | {% if hub_tls_enabled|bool %} 232 | - name: {{ hub_tls_secret_name }} 233 | mountPath: /var/run/secrets/{{ hub_tls_secret_name }}/tls.crt 234 | {% endif %} 235 | initContainers: 236 | - command: 237 | - chown 238 | - -R 239 | - {{ hub_uid }}:root 240 | - {{ hub_database_volume_path }} 241 | - {{ hub_bucket_volume_path }} 242 | image: "{{ hub_image_fqin }}" 243 | imagePullPolicy: "{{ image_pull_policy }}" 244 | name: update-perms 245 | resources: 246 | limits: 247 | cpu: {{ hub_init_container_limits_cpu }} 248 | memory: {{ hub_init_container_limits_memory }} 249 | requests: 250 | cpu: {{ hub_init_container_requests_cpu }} 251 | memory: {{ hub_init_container_requests_memory }} 252 | securityContext: 253 | runAsUser: 0 254 | volumeMounts: 255 | - mountPath: {{ hub_database_volume_path }} 256 | name: {{ hub_database_volume_name }} 257 | - mountPath: {{ hub_bucket_volume_path }} 258 | name: {{ hub_bucket_volume_name }} 259 | volumes: 260 | {% if rwx_supported|bool %} 261 | - name: {{ cache_data_volume_name }} 262 | persistentVolumeClaim: 263 | claimName: {{ cache_data_volume_claim_name }} 264 | {% endif %} 265 | - name: {{ hub_database_volume_name }} 266 | persistentVolumeClaim: 267 | claimName: {{ hub_database_volume_claim_name }} 268 | - name: {{ hub_bucket_volume_name }} 269 | persistentVolumeClaim: 270 | claimName: {{ hub_bucket_volume_claim_name }} 271 | {% if hub_tls_enabled|bool %} 272 | - name: {{ hub_tls_secret_name }} 273 | secret: 274 | secretName: {{ hub_tls_secret_name }} 275 | defaultMode: 420 276 | {% endif %} 277 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------