# frozen_string_literal: true require 'json' require 'shellwords' require 'krane/remote_logs' require 'krane/duration_parser' require 'krane/label_selector' require 'krane/rollout_conditions' module Krane class KubernetesResource attr_reader :name, :namespace, :context attr_writer :type, :deploy_started_at, :global GLOBAL = false TIMEOUT = 5.minutes LOG_LINE_COUNT = 250 SERVER_DRY_RUN_DISABLED_ERROR = /(unknown flag: --server-dry-run)|(does[\s\']n[o|']t support dry[-\s]run)|(dryRun alpha feature is disabled)/ DISABLE_FETCHING_LOG_INFO = 'DISABLE_FETCHING_LOG_INFO' DISABLE_FETCHING_EVENT_INFO = 'DISABLE_FETCHING_EVENT_INFO' DISABLED_LOG_INFO_MESSAGE = "collection is disabled by the #{DISABLE_FETCHING_LOG_INFO} env var." DISABLED_EVENT_INFO_MESSAGE = "collection is disabled by the #{DISABLE_FETCHING_EVENT_INFO} env var." DEBUG_RESOURCE_NOT_FOUND_MESSAGE = "None found. Please check your usual logging service (e.g. Splunk)." UNUSUAL_FAILURE_MESSAGE = <<~MSG It is very unusual for this resource type to fail to deploy. Please try the deploy again. If that new deploy also fails, contact your cluster administrator. MSG STANDARD_TIMEOUT_MESSAGE = <<~MSG Kubernetes will continue to attempt to deploy this resource in the cluster, but at this point it is considered unlikely that it will succeed. If you have reason to believe it will succeed, retry the deploy to continue to monitor the rollout. MSG TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX = "timeout-override" TIMEOUT_OVERRIDE_ANNOTATION_DEPRECATED = "kubernetes-deploy.shopify.io/#{TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX}" TIMEOUT_OVERRIDE_ANNOTATION = "krane.shopify.io/#{TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX}" LAST_APPLIED_ANNOTATION = "kubectl.kubernetes.io/last-applied-configuration" SENSITIVE_TEMPLATE_CONTENT = false SERVER_DRY_RUNNABLE = false SYNC_DEPENDENCIES = [] class << self def build(namespace: nil, context:, definition:, logger:, statsd_tags:, crd: nil, global_names: []) validate_definition_essentials(definition) opts = { namespace: namespace, context: context, definition: definition, logger: logger, statsd_tags: statsd_tags } if (klass = class_for_kind(definition["kind"])) return klass.new(**opts) end if crd CustomResource.new(crd: crd, **opts) else type = definition["kind"] inst = new(**opts) inst.type = type inst.global = global_names.map(&:downcase).include?(type.downcase) inst end end def class_for_kind(kind) if Krane.const_defined?(kind) Krane.const_get(kind) end rescue NameError nil end def timeout self::TIMEOUT end def kind name.demodulize end private def validate_definition_essentials(definition) debug_content = <<~STRING apiVersion: #{definition.fetch('apiVersion', '')} kind: #{definition.fetch('kind', '')} metadata: #{definition.fetch('metadata', {})}