diff --git a/lib/krane/global_deploy_task.rb b/lib/krane/global_deploy_task.rb index 081bde8bd..154f6cc20 100644 --- a/lib/krane/global_deploy_task.rb +++ b/lib/krane/global_deploy_task.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require 'kubernetes-deploy/deploy_task' +require 'kubernetes-deploy/global_deploy_task_config_validator' module Krane class GlobalDeployTask < KubernetesDeploy::DeployTask @@ -7,12 +8,12 @@ def initialize(**args) super(args.merge(allow_globals: true)) end - private - - def namespace_definition - nil + def run!(**args) + super(args.merge(task_config_validator: GlobalDeployTaskConfigValidator)) end + private + def validate_globals(resources) return unless (namespaced = resources.reject(&:global?).presence) namespaced_names = namespaced.map do |resource| diff --git a/lib/kubernetes-deploy/deploy_task.rb b/lib/kubernetes-deploy/deploy_task.rb index 501282c01..9bac7be7a 100644 --- a/lib/kubernetes-deploy/deploy_task.rb +++ b/lib/kubernetes-deploy/deploy_task.rb @@ -131,7 +131,7 @@ def initialize(namespace: nil, context:, current_sha:, logger: nil, kubectl_inst @logger = logger || KubernetesDeploy::FormattedLogger.build(namespace, context) @template_sets = TemplateSets.from_dirs_and_files(paths: template_paths, logger: @logger) - @task_config = KubernetesDeploy::DeployTaskConfig.new(context, namespace, @logger, allow_globals) + @task_config = KubernetesDeploy::TaskConfig.new(context, namespace, @logger, allow_globals && namespace.empty?) @bindings = bindings @namespace = namespace @namespace_tags = [] @@ -162,12 +162,13 @@ def run(*args) # @param prune [Boolean] Enable deletion of resources that do not appear in the template dir # # @return [nil] - def run!(verify_result: true, allow_protected_ns: false, prune: true) + def run!(verify_result: true, allow_protected_ns: false, prune: true, task_config_validator: DeployTaskConfigValidator) start = Time.now.utc @logger.reset @logger.phase_heading("Initializing deploy") - validate_configuration(allow_protected_ns: allow_protected_ns, prune: prune) + validate_configuration(allow_protected_ns: allow_protected_ns, prune: prune, task_config_validator: DeployTaskConfigValidator) + resources = discover_resources validate_resources(resources) @@ -376,8 +377,8 @@ def record_warnings(warning:, filename:) @logger.summary.add_paragraph(ColorizedString.new(warn_msg).yellow) end - def validate_configuration(allow_protected_ns:, prune:) - task_config_validator = DeployTaskConfigValidator.new(@protected_namespaces, allow_protected_ns, prune, + def validate_configuration(allow_protected_ns:, prune:, task_config_validator:) + task_config_validator = task_config_validator.new(@protected_namespaces, allow_protected_ns, prune, @task_config, kubectl, kubeclient_builder) errors = [] errors += task_config_validator.errors @@ -476,7 +477,7 @@ def apply_all(resources, prune) output_is_sensitive = resources.any?(&:sensitive_template_content?) out, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: output_is_sensitive, - use_namespace: !@task_config.allow_globals) + use_namespace: !@task_config.global_mode) if st.success? log_pruning(out) if prune @@ -557,13 +558,7 @@ def find_bad_files_from_kubectl_output(line) end def namespace_definition - @namespace_definition ||= begin - definition, _err, st = kubectl.run("get", "namespace", @namespace, use_namespace: false, - log_failure: true, raise_if_not_found: true, attempts: 3, output: 'json') - st.success? ? JSON.parse(definition, symbolize_names: true) : nil - end - rescue Kubectl::ResourceNotFoundError - nil + @task_config.namespace_definition end # make sure to never prune the ejson-keys secret diff --git a/lib/kubernetes-deploy/deploy_task_config_validator.rb b/lib/kubernetes-deploy/deploy_task_config_validator.rb index d1fb76284..cd427c20a 100644 --- a/lib/kubernetes-deploy/deploy_task_config_validator.rb +++ b/lib/kubernetes-deploy/deploy_task_config_validator.rb @@ -2,9 +2,7 @@ module KubernetesDeploy class DeployTaskConfigValidator < TaskConfigValidator def initialize(protected_namespaces, allow_protected_ns, prune, *arguments) - task_config = arguments.first - skip = task_config.allow_globals ? [:validate_namespace_exists] : [] - super(*arguments, skip: skip) + super(*arguments) @protected_namespaces = protected_namespaces @allow_protected_ns = allow_protected_ns @prune = prune diff --git a/lib/kubernetes-deploy/global_deploy_task_config_validator.rb b/lib/kubernetes-deploy/global_deploy_task_config_validator.rb new file mode 100644 index 000000000..263e485a3 --- /dev/null +++ b/lib/kubernetes-deploy/global_deploy_task_config_validator.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +module KubernetesDeploy + class GlobalDeployTaskConfigValidator < TaskConfigValidator + def initialize(protected_namespaces, allow_protected_ns, prune, *arguments) + super(*arguments, skip: [:validate_namespace_exists]) + @protected_namespaces = protected_namespaces + @allow_protected_ns = allow_protected_ns + @prune = prune + end + end +end diff --git a/lib/kubernetes-deploy/resource_cache.rb b/lib/kubernetes-deploy/resource_cache.rb index 8f7a68583..0049807d0 100644 --- a/lib/kubernetes-deploy/resource_cache.rb +++ b/lib/kubernetes-deploy/resource_cache.rb @@ -4,7 +4,7 @@ module KubernetesDeploy class ResourceCache - delegate :namespace, :context, :logger, :allow_globals, to: :@task_config + delegate :namespace, :context, :logger, :global_mode, to: :@task_config def initialize(task_config) @task_config = task_config @@ -53,7 +53,7 @@ def fetch_by_kind(kind) resource_class = KubernetesResource.class_for_kind(kind) output_is_sensitive = resource_class.nil? ? false : resource_class::SENSITIVE_TEMPLATE_CONTENT raw_json, _, st = @kubectl.run("get", kind, "--chunk-size=0", attempts: 5, output: "json", - output_is_sensitive: output_is_sensitive, use_namespace: !allow_globals) + output_is_sensitive: output_is_sensitive, use_namespace: !global_mode) raise KubectlError unless st.success? instances = {} diff --git a/lib/kubernetes-deploy/task_config.rb b/lib/kubernetes-deploy/task_config.rb index 69ba12860..60ca32dfe 100644 --- a/lib/kubernetes-deploy/task_config.rb +++ b/lib/kubernetes-deploy/task_config.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true module KubernetesDeploy class TaskConfig - attr_reader :context, :namespace + attr_reader :context, :namespace, :global_mode + attr_accessor :namespace_definition - def initialize(context, namespace, logger = nil) + def initialize(context, namespace, logger = nil, global_mode = false) @context = context @namespace = namespace @logger = logger + @global_mode = global_mode end def logger diff --git a/lib/kubernetes-deploy/task_config_validator.rb b/lib/kubernetes-deploy/task_config_validator.rb index e67d3b625..d8811e393 100644 --- a/lib/kubernetes-deploy/task_config_validator.rb +++ b/lib/kubernetes-deploy/task_config_validator.rb @@ -70,10 +70,12 @@ def validate_namespace_exists return @errors << "Namespace can not be blank" end - _, err, st = @kubectl.run("get", "namespace", "-o", "name", namespace, - use_namespace: false, log_failure: false) + definition, err, st = @kubectl.run("get", "namespace", namespace, + use_namespace: false, log_failure: false, attempts: 3, output: 'json') - unless st.success? + if st.success? + @task_config.namespace_definition = JSON.parse(definition, symbolize_names: true) + else @errors << if err.match("Error from server [(]NotFound[)]: namespace") "Could not find Namespace: #{namespace} in Context: #{context}" else diff --git a/test/helpers/resource_cache_test_helper.rb b/test/helpers/resource_cache_test_helper.rb index 7a070b8c5..fc0a13673 100644 --- a/test/helpers/resource_cache_test_helper.rb +++ b/test/helpers/resource_cache_test_helper.rb @@ -6,7 +6,7 @@ def stub_kind_get(kind, items: [], times: 1) kind, "--chunk-size=0", resp: { items: items }, - kwargs: { attempts: 5, output_is_sensitive: false }, + kwargs: { attempts: 5, output_is_sensitive: false, use_namespace: true }, times: times, ) end diff --git a/test/unit/kubernetes-deploy/deploy_task_test.rb b/test/unit/kubernetes-deploy/deploy_task_test.rb index 8bc21ef3e..d63ce8677 100644 --- a/test/unit/kubernetes-deploy/deploy_task_test.rb +++ b/test/unit/kubernetes-deploy/deploy_task_test.rb @@ -9,7 +9,7 @@ def test_that_it_has_a_version_number end def test_initializer_without_valid_file - KubernetesDeploy::Kubectl.any_instance.expects(:run).at_least_once.returns(["", "", SystemExit.new(0)]) + KubernetesDeploy::Kubectl.any_instance.expects(:run).at_least_once.returns(["{}", "", SystemExit.new(0)]) KubernetesDeploy::Kubectl.any_instance.expects(:server_version).at_least_once.returns( Gem::Version.new(KubernetesDeploy::MIN_KUBE_VERSION) ) diff --git a/test/unit/kubernetes-deploy/ejson_secret_provisioner_test.rb b/test/unit/kubernetes-deploy/ejson_secret_provisioner_test.rb index 9092b6478..3c74363e6 100644 --- a/test/unit/kubernetes-deploy/ejson_secret_provisioner_test.rb +++ b/test/unit/kubernetes-deploy/ejson_secret_provisioner_test.rb @@ -109,6 +109,7 @@ def stub_dry_run_validation_request output_is_sensitive: true, retry_whitelist: [:client_timeout], attempts: 3, + use_namespace: true }) end @@ -120,6 +121,7 @@ def stub_server_dry_run_validation_request output_is_sensitive: true, retry_whitelist: [:client_timeout], attempts: 3, + use_namespace: true, }) end diff --git a/test/unit/kubernetes-deploy/kubernetes_resource/horizontal_pod_autoscaler_test.rb b/test/unit/kubernetes-deploy/kubernetes_resource/horizontal_pod_autoscaler_test.rb index 3fa3e6d3d..42590b0b3 100644 --- a/test/unit/kubernetes-deploy/kubernetes_resource/horizontal_pod_autoscaler_test.rb +++ b/test/unit/kubernetes-deploy/kubernetes_resource/horizontal_pod_autoscaler_test.rb @@ -7,7 +7,7 @@ class HorizontalPodAutoscalerTest < KubernetesDeploy::TestCase # We can't get integration coverage for HPA right now because the metrics server just isn't reliable enough on our CI def test_hpa_is_whitelisted_for_pruning KubernetesDeploy::Kubectl.any_instance.expects("run") - .with("get", "CustomResourceDefinition", output: "json", attempts: 5) + .with("get", "CustomResourceDefinition", output: "json", attempts: 5, use_namespace: false) .returns(['{ "items": [] }', "", SystemExit.new(0)]) task = KubernetesDeploy::DeployTask.new(namespace: 'test', context: KubeclientHelper::TEST_CONTEXT, current_sha: 'foo', template_paths: [''], logger: logger) diff --git a/test/unit/kubernetes-deploy/kubernetes_resource_test.rb b/test/unit/kubernetes-deploy/kubernetes_resource_test.rb index ccd42c9dd..f3c4f05a4 100644 --- a/test/unit/kubernetes-deploy/kubernetes_resource_test.rb +++ b/test/unit/kubernetes-deploy/kubernetes_resource_test.rb @@ -411,7 +411,7 @@ def test_disappeared_is_true_if_resource_has_been_deployed_and_404s def test_disappeared_is_false_if_resource_has_been_deployed_and_we_get_a_server_error dummy = DummyResource.new - cache = KubernetesDeploy::ResourceCache.new(task_config: task_config(namespace: 'test', context: 'minikube')) + cache = KubernetesDeploy::ResourceCache.new(task_config(namespace: 'test', context: 'minikube')) KubernetesDeploy::Kubectl.any_instance.expects(:run).returns(["", "NotFound", stub(success?: false)]).twice dummy.sync(cache) diff --git a/test/unit/resource_cache_test.rb b/test/unit/resource_cache_test.rb index 53acfab12..a8dd9c814 100644 --- a/test/unit/resource_cache_test.rb +++ b/test/unit/resource_cache_test.rb @@ -37,7 +37,7 @@ def test_get_all_populates_cache_and_returns_array_of_instance_hashes end def test_if_kubectl_error_then_empty_result_returned_but_not_cached - stub_kubectl_response('get', 'FakeConfigMap', '--chunk-size=0', kwargs: { attempts: 5, output_is_sensitive: false }, + stub_kubectl_response('get', 'FakeConfigMap', '--chunk-size=0', kwargs: { use_namespace: true, attempts: 5, output_is_sensitive: false }, success: false, resp: { "items" => [] }, err: 'no', times: 4) # All of these calls should attempt the request again (see the 'times' arg above)