Class: K8s::Stack
Overview
Usage: customize the LABEL and CHECKSUM_ANNOTATION
Constant Summary collapse
- LABEL =
'k8s.kontena.io/stack'
- CHECKSUM_ANNOTATION =
'k8s.kontena.io/stack-checksum'
- PRUNE_IGNORE =
[ 'v1:ComponentStatus', # apiserver ignores GET /v1/componentstatuses?labelSelector=... and returns all resources 'v1:Endpoints', # inherits stack label from service, but not checksum annotation ]
Constants included from Logging
Logging::LOG_LEVEL, Logging::LOG_TARGET
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#resources ⇒ Object
readonly
Returns the value of attribute resources.
Class Method Summary collapse
- .apply(name, path, client, prune: true, **options) ⇒ Object
-
.delete(name, client, **options) ⇒ Object
Remove any installed stack resources.
- .load(name, path, **options) ⇒ K8s::Stack
Instance Method Summary collapse
- #apply(client, prune: true) ⇒ Array<K8s::Resource>
- #checksum ⇒ Object
-
#delete(client) ⇒ Object
Delete all stack resources.
-
#initialize(name, resources = [], debug: false, label: LABEL, checksum_annotation: CHECKSUM_ANNOTATION) ⇒ Stack
constructor
A new instance of Stack.
-
#keep_resource!(resource) ⇒ Object
key MUST NOT include resource.apiVersion: the same kind can be aliased in different APIs.
- #keep_resource?(resource) ⇒ Boolean
- #prepare_resource(resource, base_resource: nil) ⇒ K8s::Resource
-
#prune(client, keep_resources:) ⇒ Object
Delete all stack resources that were not applied.
Methods included from Logging
Methods included from Logging::ModuleMethods
#debug!, #log_level, #log_level=, #quiet!, #verbose!
Constructor Details
#initialize(name, resources = [], debug: false, label: LABEL, checksum_annotation: CHECKSUM_ANNOTATION) ⇒ Stack
Returns a new instance of Stack.
41 42 43 44 45 46 47 48 49 |
# File 'lib/k8s/stack.rb', line 41 def initialize(name, resources = [], debug: false, label: LABEL, checksum_annotation: CHECKSUM_ANNOTATION) @name = name @resources = resources @keep_resources = {} @label = label @checksum_annotation = checksum_annotation logger! progname: name, debug: debug end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
39 40 41 |
# File 'lib/k8s/stack.rb', line 39 def name @name end |
#resources ⇒ Object (readonly)
Returns the value of attribute resources.
39 40 41 |
# File 'lib/k8s/stack.rb', line 39 def resources @resources end |
Class Method Details
.apply(name, path, client, prune: true, **options) ⇒ Object
27 28 29 |
# File 'lib/k8s/stack.rb', line 27 def self.apply(name, path, client, prune: true, **) load(name, path, **).apply(client, prune: prune) end |
.delete(name, client, **options) ⇒ Object
Remove any installed stack resources.
35 36 37 |
# File 'lib/k8s/stack.rb', line 35 def self.delete(name, client, **) new(name, **).delete(client) end |
.load(name, path, **options) ⇒ K8s::Stack
18 19 20 21 |
# File 'lib/k8s/stack.rb', line 18 def self.load(name, path, **) resources = K8s::Resource.from_files(path) new(name, resources, **) end |
Instance Method Details
#apply(client, prune: true) ⇒ Array<K8s::Resource>
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/k8s/stack.rb', line 71 def apply(client, prune: true) server_resources = client.get_resources(resources) resources.zip(server_resources).map do |resource, server_resource| if server_resource # keep server checksum for comparison # NOTE: this will not compare equal for resources with arrays containing hashes with default values applied by the server # however, that will just cause extra PUTs, so it doesn't have any functional effects compare_resource = server_resource.merge(resource).merge(metadata: { labels: { @label => name }, }) end if !server_resource logger.info "Create resource #{resource.apiVersion}:#{resource.kind}/#{resource..name} in namespace #{resource..namespace} with checksum=#{checksum}" keep_resource! client.create_resource(prepare_resource(resource)) elsif server_resource != compare_resource logger.info "Update resource #{resource.apiVersion}:#{resource.kind}/#{resource..name} in namespace #{resource..namespace} with checksum=#{checksum}" keep_resource! client.update_resource(prepare_resource(resource, base_resource: server_resource)) else logger.info "Keep resource #{resource.apiVersion}:#{resource.kind}/#{resource..name} in namespace #{resource..namespace} with checksum=#{compare_resource..annotations[@checksum_annotation]}" keep_resource! compare_resource end end prune(client, keep_resources: true) if prune end |
#checksum ⇒ Object
51 52 53 |
# File 'lib/k8s/stack.rb', line 51 def checksum @checksum ||= SecureRandom.hex(16) end |
#delete(client) ⇒ Object
Delete all stack resources
134 135 136 |
# File 'lib/k8s/stack.rb', line 134 def delete(client) prune(client, keep_resources: false) end |
#keep_resource!(resource) ⇒ Object
key MUST NOT include resource.apiVersion: the same kind can be aliased in different APIs
100 101 102 |
# File 'lib/k8s/stack.rb', line 100 def keep_resource!(resource) @keep_resources["#{resource.kind}:#{resource..name}@#{resource..namespace}"] = resource..annotations[@checksum_annotation] end |
#keep_resource?(resource) ⇒ Boolean
103 104 105 |
# File 'lib/k8s/stack.rb', line 103 def keep_resource?(resource) @keep_resources["#{resource.kind}:#{resource..name}@#{resource..namespace}"] == resource..annotations[@checksum_annotation] end |
#prepare_resource(resource, base_resource: nil) ⇒ K8s::Resource
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/k8s/stack.rb', line 58 def prepare_resource(resource, base_resource: nil) if base_resource resource = base_resource.merge(resource) end # add stack metadata resource.merge(metadata: { labels: { @label => name }, annotations: { @checksum_annotation => checksum }, }) end |
#prune(client, keep_resources:) ⇒ Object
Delete all stack resources that were not applied
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/k8s/stack.rb', line 108 def prune(client, keep_resources: ) client.list_resources(labelSelector: {@label => name}).each do |resource| next if PRUNE_IGNORE.include? "#{resource.apiVersion}:#{resource.kind}" resource_label = resource..labels ? resource..labels[@label] : nil resource_checksum = resource..annotations ? resource..annotations[@checksum_annotation] : nil logger.debug { "List resource #{resource.apiVersion}:#{resource.kind}/#{resource..name} in namespace #{resource..namespace} with checksum=#{resource_checksum}" } if resource_label != name # apiserver did not respect labelSelector elsif keep_resources && keep_resource?(resource) # resource is up-to-date else logger.info "Delete resource #{resource.apiVersion}:#{resource.kind}/#{resource..name} in namespace #{resource..namespace}" begin client.delete_resource(resource) rescue K8s::Error::NotFound # assume aliased objects in multiple API groups, like for Deployments # alternatively, a custom resource whose definition was already deleted earlier end end end end |