From d7c7ebf50a0c1c52d04ecaab78c652f7786423ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Cunha?= Date: Wed, 31 Jul 2019 11:58:38 +0000 Subject: [PATCH] Allow knative do be uninstalled: - After uninstalling the knative helm chart it's necessary to also remove some leftover resources to allow the cluster to be clean and knative to be reinstalleable. - Adds knative uninstall disclaimer - Uninstall ksvc before uninstalling knative Make list of Knative and Ingres resources explicit - To avoid deleting unwanted resources we are listing exact which resources will be deleted rather than simply deleting any resource that contains istio or knative words. --- ...install_application_confirmation_modal.vue | 4 +- .../clusters/applications/cert_manager.rb | 2 +- app/models/clusters/applications/knative.rb | 54 ++++++++++++++-- .../clusters/applications/prometheus.rb | 13 ++++ ...ernetes-applications-uninstall-knative.yml | 5 ++ config/knative/api_resources.yml | 64 +++++++++++++++++++ doc/user/clusters/applications.md | 1 + lib/gitlab/kubernetes/helm/delete_command.rb | 9 ++- lib/gitlab/kubernetes/helm/install_command.rb | 12 +--- locale/gitlab.pot | 2 +- .../projects/clusters/applications_spec.rb | 2 +- .../applications/cert_manager_spec.rb | 2 +- .../clusters/applications/knative_spec.rb | 42 +++++++++++- 13 files changed, 188 insertions(+), 24 deletions(-) create mode 100644 changelogs/unreleased/60668-kubernetes-applications-uninstall-knative.yml create mode 100644 config/knative/api_resources.yml diff --git a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue b/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue index 920439ebb23..2ff6d5e32e2 100644 --- a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue +++ b/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue @@ -13,7 +13,9 @@ const CUSTOM_APP_WARNING_TEXT = { ), [PROMETHEUS]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'), [RUNNER]: s__('ClusterIntegration|Any running pipelines will be canceled.'), - [KNATIVE]: s__('ClusterIntegration|The associated IP will be deleted and cannot be restored.'), + [KNATIVE]: s__( + 'ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications.', + ), [JUPYTER]: s__( 'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.', ), diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb index d6a7d1d2bdd..7d5a6dec519 100644 --- a/app/models/clusters/applications/cert_manager.rb +++ b/app/models/clusters/applications/cert_manager.rb @@ -44,7 +44,7 @@ module Clusters private def post_install_script - ["/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml"] + ["kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml"] end def cluster_issuer_file diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index 5df4812bd25..96f526e8a36 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -7,6 +7,7 @@ module Clusters REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'.freeze FETCH_IP_ADDRESS_DELAY = 30.seconds + API_RESOURCES_PATH = 'config/knative/api_resources.yml' self.table_name = 'clusters_applications_knative' @@ -46,12 +47,6 @@ module Clusters { "domain" => hostname }.to_yaml end - # Handled in a new issue: - # https://gitlab.com/gitlab-org/gitlab-ce/issues/59369 - def allowed_to_uninstall? - false - end - def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -76,12 +71,59 @@ module Clusters cluster.kubeclient.get_service('istio-ingressgateway', 'istio-system') end + def uninstall_command + Gitlab::Kubernetes::Helm::DeleteCommand.new( + name: name, + rbac: cluster.platform_kubernetes_rbac?, + files: files, + predelete: delete_knative_services_and_metrics, + postdelete: delete_knative_istio_leftovers + ) + end + private + def delete_knative_services_and_metrics + delete_knative_services + delete_knative_istio_metrics.to_a + end + + def delete_knative_services + cluster.kubernetes_namespaces.map do |kubernetes_namespace| + "kubectl delete ksvc --all -n #{kubernetes_namespace.namespace}" + end + end + + def delete_knative_istio_leftovers + delete_knative_namespaces + delete_knative_and_istio_crds + end + + def delete_knative_namespaces + [ + "kubectl delete --ignore-not-found ns knative-serving", + "kubectl delete --ignore-not-found ns knative-build" + ] + end + + def delete_knative_and_istio_crds + api_resources.map do |crd| + "kubectl delete --ignore-not-found crd #{crd}" + end + end + + # returns an array of CRDs to be postdelete since helm does not + # manage the CRDs it creates. + def api_resources + @api_resources ||= YAML.safe_load(File.read(Rails.root.join(API_RESOURCES_PATH))) + end + def install_knative_metrics ["kubectl apply -f #{METRICS_CONFIG}"] if cluster.application_prometheus_available? end + def delete_knative_istio_metrics + ["kubectl delete --ignore-not-found -f #{METRICS_CONFIG}"] if cluster.application_prometheus_available? + end + def verify_cluster? cluster&.application_helm_available? && cluster&.platform_kubernetes_rbac? end diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index 805c8a73f8c..f5375d29f3a 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -59,6 +59,15 @@ module Clusters ) end + def uninstall_command + Gitlab::Kubernetes::Helm::DeleteCommand.new( + name: name, + rbac: cluster.platform_kubernetes_rbac?, + files: files, + predelete: delete_knative_istio_metrics.to_a + ) + end + # Returns a copy of files where the values of 'values.yaml' # are replaced by the argument. # @@ -97,6 +106,10 @@ module Clusters def install_knative_metrics ["kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}"] if cluster.application_knative_available? end + + def delete_knative_istio_metrics + ["kubectl delete -f #{Clusters::Applications::Knative::METRICS_CONFIG}"] if cluster.application_knative_available? + end end end end diff --git a/changelogs/unreleased/60668-kubernetes-applications-uninstall-knative.yml b/changelogs/unreleased/60668-kubernetes-applications-uninstall-knative.yml new file mode 100644 index 00000000000..c33dc0f50cd --- /dev/null +++ b/changelogs/unreleased/60668-kubernetes-applications-uninstall-knative.yml @@ -0,0 +1,5 @@ +--- +title: Allow Knative to be uninstalled from the UI +merge_request: 30458 +author: +type: added diff --git a/config/knative/api_resources.yml b/config/knative/api_resources.yml new file mode 100644 index 00000000000..43427b730db --- /dev/null +++ b/config/knative/api_resources.yml @@ -0,0 +1,64 @@ +--- + +- meshpolicies.authentication.istio.io +- policies.authentication.istio.io +- adapters.config.istio.io +- apikeys.config.istio.io +- attributemanifests.config.istio.io +- authorizations.config.istio.io +- bypasses.config.istio.io +- podautoscalers.autoscaling.internal.knative.dev +- builds.build.knative.dev +- buildtemplates.build.knative.dev +- clusterbuildtemplates.build.knative.dev +- images.caching.internal.knative.dev +- certificates.networking.internal.knative.dev +- clusteringresses.networking.internal.knative.dev +- serverlessservices.networking.internal.knative.dev +- configurations.serving.knative.dev +- revisions.serving.knative.dev +- routes.serving.knative.dev +- services.serving.knative.dev +- checknothings.config.istio.io +- circonuses.config.istio.io +- deniers.config.istio.io +- edges.config.istio.io +- fluentds.config.istio.io +- handlers.config.istio.io +- httpapispecbindings.config.istio.io +- httpapispecs.config.istio.io +- instances.config.istio.io +- kubernetesenvs.config.istio.io +- kuberneteses.config.istio.io +- listcheckers.config.istio.io +- listentries.config.istio.io +- logentries.config.istio.io +- memquotas.config.istio.io +- metrics.config.istio.io +- noops.config.istio.io +- opas.config.istio.io +- prometheuses.config.istio.io +- quotas.config.istio.io +- quotaspecbindings.config.istio.io +- quotaspecs.config.istio.io +- rbacs.config.istio.io +- redisquotas.config.istio.io +- reportnothings.config.istio.io +- rules.config.istio.io +- servicecontrolreports.config.istio.io +- servicecontrols.config.istio.io +- signalfxs.config.istio.io +- solarwindses.config.istio.io +- stackdrivers.config.istio.io +- statsds.config.istio.io +- stdios.config.istio.io +- templates.config.istio.io +- tracespans.config.istio.io +- destinationrules.networking.istio.io +- envoyfilters.networking.istio.io +- gateways.networking.istio.io +- serviceentries.networking.istio.io +- virtualservices.networking.istio.io +- rbacconfigs.rbac.istio.io +- servicerolebindings.rbac.istio.io +- serviceroles.rbac.istio.io \ No newline at end of file diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 1bc43f4b7a4..7c24f6db86f 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -255,6 +255,7 @@ The applications below can be uninstalled. | GitLab Runner | 12.2+ | Any running pipelines will be canceled. | | Ingress | 12.1+ | The associated load balancer and IP will be deleted and cannot be restored. Furthermore, it can only be uninstalled if JupyterHub is not installed. | | JupyterHub | 12.1+ | All data not committed to GitLab will be deleted and cannot be restored. | +| Knative | 12.1+ | The associated IP will be deleted and cannot be restored. | | Prometheus | 11.11+ | All data will be deleted and cannot be restored. | To uninstall an application: diff --git a/lib/gitlab/kubernetes/helm/delete_command.rb b/lib/gitlab/kubernetes/helm/delete_command.rb index 876994d2678..aeba4a54b6d 100644 --- a/lib/gitlab/kubernetes/helm/delete_command.rb +++ b/lib/gitlab/kubernetes/helm/delete_command.rb @@ -7,19 +7,24 @@ module Gitlab include BaseCommand include ClientCommand + attr_reader :predelete, :postdelete attr_accessor :name, :files - def initialize(name:, rbac:, files:) + def initialize(name:, rbac:, files:, predelete: nil, postdelete: nil) @name = name @files = files @rbac = rbac + @predelete = predelete + @postdelete = postdelete end def generate_script super + [ init_command, wait_for_tiller_command, - delete_command + predelete, + delete_command, + postdelete ].compact.join("\n") end diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb index 9744a5f3d8a..f572bc43533 100644 --- a/lib/gitlab/kubernetes/helm/install_command.rb +++ b/lib/gitlab/kubernetes/helm/install_command.rb @@ -27,9 +27,9 @@ module Gitlab wait_for_tiller_command, repository_command, repository_update_command, - preinstall_command, + preinstall, install_command, - postinstall_command + postinstall ].compact.join("\n") end @@ -58,14 +58,6 @@ module Gitlab command.shelljoin end - def preinstall_command - preinstall.join("\n") if preinstall - end - - def postinstall_command - postinstall.join("\n") if postinstall - end - def install_flag ['--install'] end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 878ee5bc344..709226e686c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2815,7 +2815,7 @@ msgstr "" msgid "ClusterIntegration|The URL used to access the Kubernetes API." msgstr "" -msgid "ClusterIntegration|The associated IP will be deleted and cannot be restored." +msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications." msgstr "" msgid "ClusterIntegration|The associated certifcate will be deleted and cannot be restored." diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index d0182270d82..de97c8a8bc0 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -126,7 +126,7 @@ describe 'Clusters Applications', :js do it 'shows status transition' do page.within('.js-cluster-application-row-knative') do expect(domainname_form_value).to eq('domain.example.org') - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed') + expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') end expect(page).to have_content('Knative was successfully installed on your Kubernetes cluster') diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb index 8d853a04e33..6e1f80cadc2 100644 --- a/spec/models/clusters/applications/cert_manager_spec.rb +++ b/spec/models/clusters/applications/cert_manager_spec.rb @@ -48,7 +48,7 @@ describe Clusters::Applications::CertManager do expect(subject.version).to eq('v0.5.2') expect(subject).to be_rbac expect(subject.files).to eq(cert_manager.files.merge(cluster_issuer_file)) - expect(subject.postinstall).to eq(['/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml']) + expect(subject.postinstall).to eq(['kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml']) end context 'for a specific user' do diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 7f4819cbb9a..342ed907854 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -39,7 +39,7 @@ describe Clusters::Applications::Knative do describe '#can_uninstall?' do subject { knative.can_uninstall? } - it { is_expected.to be_falsey } + it { is_expected.to be_truthy } end describe '#schedule_status_update with external_ip' do @@ -129,6 +129,46 @@ describe Clusters::Applications::Knative do it_behaves_like 'a command' end + describe '#uninstall_command' do + subject { knative.uninstall_command } + + it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) } + + it "removes knative deployed services before uninstallation" do + 2.times do |i| + cluster_project = create(:cluster_project, cluster: knative.cluster) + + create(:cluster_kubernetes_namespace, + cluster: cluster_project.cluster, + cluster_project: cluster_project, + project: cluster_project.project, + namespace: "namespace_#{i}") + end + + remove_namespaced_services_script = [ + "kubectl delete ksvc --all -n #{knative.cluster.kubernetes_namespaces.first.namespace}", + "kubectl delete ksvc --all -n #{knative.cluster.kubernetes_namespaces.second.namespace}" + ] + + expect(subject.predelete).to match_array(remove_namespaced_services_script) + end + + it "initializes command with all necessary postdelete script" do + api_resources = YAML.safe_load(File.read(Rails.root.join(Clusters::Applications::Knative::API_RESOURCES_PATH))) + + remove_knative_istio_leftovers_script = [ + "kubectl delete --ignore-not-found ns knative-serving", + "kubectl delete --ignore-not-found ns knative-build" + ] + + full_delete_commands_size = api_resources.size + remove_knative_istio_leftovers_script.size + + expect(subject.postdelete).to include(*remove_knative_istio_leftovers_script) + expect(subject.postdelete.size).to eq(full_delete_commands_size) + expect(subject.postdelete[2]).to eq("kubectl delete --ignore-not-found crd #{api_resources[0]}") + end + end + describe '#files' do let(:application) { knative } let(:values) { subject[:'values.yaml'] }