matsuri 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.envrc +1 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +9 -0
- data/AGENTS.md +18 -0
- data/CHANGELOG.md +642 -0
- data/Gemfile +2 -0
- data/README.md +86 -8
- data/Rakefile +20 -0
- data/bin/console +1 -0
- data/bin/matsuri +6 -0
- data/bin/setup +2 -0
- data/docs/acceptors.md +181 -0
- data/docs/boot.md +101 -0
- data/docs/design/acceptors.md +157 -0
- data/docs/design/boot.md +131 -0
- data/docs/design/ssa.md +142 -0
- data/docs/design/stacks.md +153 -0
- data/docs/diff.md +85 -0
- data/docs/known-issues.md +19 -0
- data/docs/local-dev.md +173 -0
- data/docs/presenters.md +317 -0
- data/docs/resources.md +121 -0
- data/docs/setup.md +97 -0
- data/docs/ssa.md +149 -0
- data/docs/stacks.md +146 -0
- data/flake.lock +61 -0
- data/flake.nix +35 -0
- data/lib/matsuri/acceptor.rb +33 -0
- data/lib/matsuri/acceptors/image_tag.rb +15 -0
- data/lib/matsuri/cmd.rb +46 -0
- data/lib/matsuri/cmds/apply.rb +78 -0
- data/lib/matsuri/cmds/cli.rb +147 -0
- data/lib/matsuri/cmds/create.rb +87 -0
- data/lib/matsuri/cmds/delete.rb +86 -0
- data/lib/matsuri/cmds/diff.rb +105 -0
- data/lib/matsuri/cmds/generate.rb +24 -0
- data/lib/matsuri/cmds/kubectl.rb +16 -0
- data/lib/matsuri/cmds/recreate.rb +65 -0
- data/lib/matsuri/cmds/scale.rb +37 -0
- data/lib/matsuri/cmds/show.rb +123 -0
- data/lib/matsuri/cmds/status.rb +67 -0
- data/lib/matsuri/concerns/awaiting.rb +24 -0
- data/lib/matsuri/concerns/diffable.rb +135 -0
- data/lib/matsuri/concerns/export_manifest.rb +56 -0
- data/lib/matsuri/concerns/import_manifest.rb +64 -0
- data/lib/matsuri/concerns/pod_template.rb +37 -0
- data/lib/matsuri/concerns/pods/spread_pods.rb +47 -0
- data/lib/matsuri/concerns/pods/spread_topology.rb +63 -0
- data/lib/matsuri/concerns/registry_helpers.rb +59 -0
- data/lib/matsuri/config.rb +101 -0
- data/lib/matsuri/core_ext.rb +107 -0
- data/lib/matsuri/dsl/cluster/aggregated_cluster_role.rb +75 -0
- data/lib/matsuri/dsl/cluster/binding.rb +118 -0
- data/lib/matsuri/dsl/cluster/cluster_role.rb +41 -0
- data/lib/matsuri/dsl/cluster/role.rb +49 -0
- data/lib/matsuri/dsl/cluster/scope.rb +245 -0
- data/lib/matsuri/dsl/cluster/service_account.rb +76 -0
- data/lib/matsuri/dsl/concerns/default_binding.rb +34 -0
- data/lib/matsuri/dsl/concerns/manifest_set.rb +21 -0
- data/lib/matsuri/dsl/concerns/metadata.rb +56 -0
- data/lib/matsuri/dsl/concerns/rbac_rules.rb +39 -0
- data/lib/matsuri/initializer.rb +41 -0
- data/lib/matsuri/kubernetes/base.rb +107 -0
- data/lib/matsuri/kubernetes/config_map.rb +100 -0
- data/lib/matsuri/kubernetes/cron_job.rb +115 -0
- data/lib/matsuri/kubernetes/daemon_set.rb +62 -0
- data/lib/matsuri/kubernetes/deployment.rb +65 -0
- data/lib/matsuri/kubernetes/endpoints.rb +57 -0
- data/lib/matsuri/kubernetes/horizontal_pod_autoscaler.rb +116 -0
- data/lib/matsuri/kubernetes/imported_manifest.rb +61 -0
- data/lib/matsuri/kubernetes/ingress.rb +66 -0
- data/lib/matsuri/kubernetes/persistent_volume.rb +56 -0
- data/lib/matsuri/kubernetes/persistent_volume_claim.rb +41 -0
- data/lib/matsuri/kubernetes/pod.rb +355 -0
- data/lib/matsuri/kubernetes/replica_set.rb +46 -0
- data/lib/matsuri/kubernetes/secret.rb +86 -0
- data/lib/matsuri/kubernetes/service.rb +49 -0
- data/lib/matsuri/kubernetes/stateful_set.rb +75 -0
- data/lib/matsuri/kubernetes/storage_class.rb +45 -0
- data/lib/matsuri/platform.rb +38 -0
- data/lib/matsuri/presenter.rb +47 -0
- data/lib/matsuri/presenters/helpers.rb +39 -0
- data/lib/matsuri/presenters/prune_empty.rb +59 -0
- data/lib/matsuri/presenters/seed_image.rb +28 -0
- data/lib/matsuri/presenters/seed_replicas.rb +25 -0
- data/lib/matsuri/presenters/seed_resources.rb +29 -0
- data/lib/matsuri/registry.rb +267 -0
- data/lib/matsuri/shell_out.rb +119 -0
- data/lib/matsuri/stack.rb +276 -0
- data/lib/matsuri/task.rb +20 -0
- data/lib/matsuri/tasks/cluster.rb +79 -0
- data/lib/matsuri/tasks/docker.rb +25 -0
- data/lib/matsuri/tasks/hash_diff.rb +61 -0
- data/lib/matsuri/tasks/kubectl.rb +21 -0
- data/lib/matsuri/tasks/kubectl_ops/apply.rb +24 -0
- data/lib/matsuri/tasks/kubectl_ops/create.rb +27 -0
- data/lib/matsuri/tasks/kubectl_ops/delete.rb +21 -0
- data/lib/matsuri/tasks/kubectl_ops/diff.rb +47 -0
- data/lib/matsuri/tasks/kubectl_ops/get.rb +36 -0
- data/lib/matsuri/tasks/kubectl_ops/recreate.rb +35 -0
- data/lib/matsuri/tasks/kubectl_ops/replace.rb +29 -0
- data/lib/matsuri/tasks/kubectl_ops/rollout_status.rb +22 -0
- data/lib/matsuri/tasks/kubectl_ops/scale.rb +22 -0
- data/lib/matsuri/tasks/kubectl_ops/server_side_apply.rb +32 -0
- data/lib/matsuri/tasks/kubectl_ops/status.rb +20 -0
- data/lib/matsuri/tasks/kubectl_ops.rb +218 -0
- data/lib/matsuri/tasks/pod.rb +56 -0
- data/lib/matsuri/tasks/stacks/seed.rb +93 -0
- data/lib/matsuri/tasks/stacks.rb +31 -0
- data/lib/matsuri/transform/algebra.rb +52 -0
- data/lib/matsuri/transform/callable.rb +54 -0
- data/lib/matsuri/transform/combinators.rb +61 -0
- data/lib/matsuri/transform/env.rb +43 -0
- data/lib/matsuri/transform/kubernetes.rb +89 -0
- data/lib/matsuri/transform.rb +20 -0
- data/lib/matsuri/version.rb +3 -1
- data/lib/matsuri.rb +158 -2
- data/matsuri.gemspec +20 -7
- data/tests/bin/minikube-start.sh +27 -0
- data/tests/bin/probe-save-config.sh +46 -0
- data/tests/bin/probe-ssa.sh +206 -0
- data/tests/example/.gitignore +1 -0
- data/tests/example/Gemfile +7 -0
- data/tests/example/README.md +91 -0
- data/tests/example/bin/matsuri +14 -0
- data/tests/example/config/initializer.rb +12 -0
- data/tests/example/config/matsuri.rb +9 -0
- data/tests/example/config/platform.rb +39 -0
- data/tests/example/lib/acme/nodes/dedicated.rb +26 -0
- data/tests/example/lib/acme/nodes/preemptible.rb +17 -0
- data/tests/example/lib/acme/nodes/required_cores.rb +26 -0
- data/tests/example/lib/acme/nodes/resource_class.rb +26 -0
- data/tests/example/lib/acme/pods/cacerts.rb +15 -0
- data/tests/example/lib/acme/pods/healthcheck.rb +36 -0
- data/tests/example/lib/acme.rb +26 -0
- data/tests/example/platform/config_maps/billing-settings.rb +8 -0
- data/tests/example/platform/config_maps/cacerts.rb +9 -0
- data/tests/example/platform/deployments/billing-web.rb +5 -0
- data/tests/example/platform/deployments/checkout-web.rb +6 -0
- data/tests/example/platform/namespaces/billing-dev.yaml +7 -0
- data/tests/example/platform/namespaces/checkout-dev.yaml +7 -0
- data/tests/example/platform/pods/billing.rb +31 -0
- data/tests/example/platform/pods/checkout.rb +27 -0
- data/tests/example/platform/services/billing-web.rb +6 -0
- data/tests/example/platform/services/checkout-web.rb +4 -0
- data/tests/example/platform/stacks/billing.rb +8 -0
- data/tests/example/platform/stacks/checkout.rb +19 -0
- data/tests/k8s/save-config/deployment-declared.yaml +19 -0
- data/tests/k8s/save-config/deployment-seeded.yaml +20 -0
- data/tests/k8s/save-config/namespace.yaml +4 -0
- data/tests/k8s/ssa/declared-bumped.yaml +22 -0
- data/tests/k8s/ssa/declared.yaml +22 -0
- data/tests/k8s/ssa/namespace.yaml +4 -0
- data/tests/k8s/ssa/seed-via-create.yaml +24 -0
- metadata +288 -20
- data/.travis.yml +0 -4
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Matsuri
|
|
4
|
+
module Transform
|
|
5
|
+
# Combinators are Element -> Element: they take a transform step and
|
|
6
|
+
# return a transform step, so selectivity lives in the wrapper and the
|
|
7
|
+
# wrapped element stays total. They are machinery, not presenters —
|
|
8
|
+
# never registry-named; names cross the declaration boundary, machinery
|
|
9
|
+
# does not.
|
|
10
|
+
module Combinators
|
|
11
|
+
# Applies its element only to manifests meeting the criteria —
|
|
12
|
+
# identity otherwise. Criteria are the conjunction of the declared
|
|
13
|
+
# {kind:, name:, namespace:} constraints, the same addressing grammar
|
|
14
|
+
# import lookup uses; an undeclared key constrains nothing, an unknown
|
|
15
|
+
# key is an error.
|
|
16
|
+
class Match
|
|
17
|
+
CRITERIA_KEYS = %w[kind name namespace].freeze
|
|
18
|
+
|
|
19
|
+
attr_reader :criteria, :element
|
|
20
|
+
|
|
21
|
+
def initialize(criteria, element)
|
|
22
|
+
@criteria = Map.new(criteria)
|
|
23
|
+
unknown = @criteria.keys - CRITERIA_KEYS
|
|
24
|
+
raise ArgumentError, "unknown match criteria #{unknown.inspect}; use #{CRITERIA_KEYS.join(', ')}" if unknown.any?
|
|
25
|
+
|
|
26
|
+
@element = Callable.new([element])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call(manifest)
|
|
30
|
+
return manifest unless matches?(manifest)
|
|
31
|
+
|
|
32
|
+
element.call(manifest)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def matches?(manifest)
|
|
36
|
+
m = Map === manifest ? manifest : Map.new(manifest)
|
|
37
|
+
(criteria[:kind].nil? || m.get(:kind) == criteria[:kind]) &&
|
|
38
|
+
(criteria[:name].nil? || m.get(:metadata, :name) == criteria[:name]) &&
|
|
39
|
+
(criteria[:namespace].nil? || m.get(:metadata, :namespace) == criteria[:namespace])
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The declaration-surface vocabulary. Sugars grow on demand.
|
|
44
|
+
module Helpers
|
|
45
|
+
def match(criteria, element)
|
|
46
|
+
Match.new(criteria, element)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def match_kind(kind, element)
|
|
50
|
+
Match.new({ kind: kind }, element)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def match_deployments(element)
|
|
54
|
+
match_kind('Deployment', element)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
extend Helpers
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Matsuri
|
|
4
|
+
module Transform
|
|
5
|
+
# The array <-> map isomorphism for container env entries. Manifests
|
|
6
|
+
# carry env as an ordered array of {name:, value:|valueFrom:} entries;
|
|
7
|
+
# the map side exists so entries can be updated by key. The round trip
|
|
8
|
+
# preserves order, appends new keys at the end, and compacts duplicate
|
|
9
|
+
# names (last write wins), so it is not a 1-to-1 mapping.
|
|
10
|
+
module Env
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
def update_kv(kv, key, value)
|
|
14
|
+
updated_kv = env_to_map(kv).update(key, value)
|
|
15
|
+
expand_env(updated_kv)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def env_to_map(kv)
|
|
19
|
+
kv.inject(Map.new) do |acc, entry|
|
|
20
|
+
case
|
|
21
|
+
when entry.key?('value')
|
|
22
|
+
acc[entry['name']] = entry['value']
|
|
23
|
+
when entry.key?('valueFrom')
|
|
24
|
+
acc[entry['name']] = entry['valueFrom']
|
|
25
|
+
else
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
acc
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def expand_env(hash)
|
|
33
|
+
hash.map do |k, v|
|
|
34
|
+
if v.is_a?(Hash)
|
|
35
|
+
{ name: k, valueFrom: v }
|
|
36
|
+
else
|
|
37
|
+
{ name: k, value: v.to_s }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Matsuri
|
|
4
|
+
module Transform
|
|
5
|
+
# The Kubernetes dialect: Algebra + Env ops addressed to the manifest
|
|
6
|
+
# shapes Kubernetes actually uses — container lists, env entries,
|
|
7
|
+
# labels, volumes, node affinity. Well-known paths are constants so a
|
|
8
|
+
# misspelled path cannot exist at a call site.
|
|
9
|
+
module Kubernetes
|
|
10
|
+
include Algebra
|
|
11
|
+
include Env
|
|
12
|
+
extend self
|
|
13
|
+
|
|
14
|
+
NODE_SELECTOR_TERMS_PATH = %i[
|
|
15
|
+
affinity
|
|
16
|
+
nodeAffinity
|
|
17
|
+
requiredDuringSchedulingIgnoredDuringExecution
|
|
18
|
+
nodeSelectorTerms
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
# The pod spec within a pod-template workload or a bare pod manifest.
|
|
22
|
+
def pod_spec_path(manifest)
|
|
23
|
+
manifest.get(:spec, :template, :spec) ? %i[spec template spec] : %i[spec]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Set the env variable of a container, defaults to container 0
|
|
27
|
+
def set_env(spec, key, value, container: 0)
|
|
28
|
+
path = [:containers, container, :env]
|
|
29
|
+
spec.set(path, update_kv(spec.get(path), key, value))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Set the env variables for all containers
|
|
33
|
+
def set_all_env(spec, key, value)
|
|
34
|
+
size = spec.get(:containers).length
|
|
35
|
+
if size > 0 then
|
|
36
|
+
(0..size - 1).inject(spec) do |spec, i|
|
|
37
|
+
set_env(spec, key, value, container: i)
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
# If there are no containers, there are other things wrong with the manifest.
|
|
41
|
+
# However, return the same spec to satisify the zero-point property of the transform
|
|
42
|
+
spec
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def set_resources(spec, resources_spec, container_name:)
|
|
47
|
+
container_num = index_by_name!(spec.get(:containers), container_name)
|
|
48
|
+
spec.set([:containers, container_num, :resources], resources_spec)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Curried form of set_resources: names the container and the resource
|
|
52
|
+
# spec up front, returns the spec -> spec step.
|
|
53
|
+
def with_resources(name, resource_spec)
|
|
54
|
+
->(spec) { set_resources(spec, resource_spec, container_name: name) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Set a label
|
|
58
|
+
def set_label(metadata, key, value)
|
|
59
|
+
metadata.set(:labels, key, value)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Append a volume mount to a container, defaults to container 0
|
|
63
|
+
def append_volume_mount(spec, volume_mount_spec, container: 0)
|
|
64
|
+
append_to(spec, [:containers, container, :volumeMounts], volume_mount_spec)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Append a volume
|
|
68
|
+
def append_volume(spec, volume_spec)
|
|
69
|
+
append_to(spec, [:volumes], volume_spec)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def without_container(spec, name:)
|
|
73
|
+
update_in(spec, [:containers]) { |x| except_entry_by_name(x, name: name) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Helper for updating node selector terms
|
|
77
|
+
def update_in_node_selector_terms(spec, &f)
|
|
78
|
+
update_in(spec, NODE_SELECTOR_TERMS_PATH, &f)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Helper for updating node affinity terms
|
|
82
|
+
def append_node_affinity(spec, match_expressions)
|
|
83
|
+
update_in(spec, NODE_SELECTOR_TERMS_PATH) do |terms|
|
|
84
|
+
terms + [match_expressions]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Matsuri
|
|
4
|
+
# Pure manifest transformation strata. Nothing under this namespace shells
|
|
5
|
+
# out or touches a cluster; everything operates on plain data (Map / Hash /
|
|
6
|
+
# Array) and composes as values.
|
|
7
|
+
module Transform
|
|
8
|
+
autoload :Algebra, 'matsuri/transform/algebra'
|
|
9
|
+
autoload :Callable, 'matsuri/transform/callable'
|
|
10
|
+
autoload :Env, 'matsuri/transform/env'
|
|
11
|
+
autoload :Kubernetes, 'matsuri/transform/kubernetes'
|
|
12
|
+
autoload :Combinators, 'matsuri/transform/combinators'
|
|
13
|
+
|
|
14
|
+
# Raised by strict by-name lookups when no entry matches.
|
|
15
|
+
class EntryNotFound < StandardError; end
|
|
16
|
+
|
|
17
|
+
# Raised by path-addressed ops when nothing exists at the path.
|
|
18
|
+
class PathNotFound < StandardError; end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/matsuri/version.rb
CHANGED
data/lib/matsuri.rb
CHANGED
|
@@ -1,5 +1,161 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'matsuri/core_ext'
|
|
2
4
|
|
|
3
5
|
module Matsuri
|
|
4
|
-
#
|
|
6
|
+
# Raised when a declared hole — a let() with no sensible default — is read
|
|
7
|
+
# before a definition fills it. Inherits NoMethodError: the definition has
|
|
8
|
+
# no working version of that let. The raise lands in the code that expected
|
|
9
|
+
# the value, so the implementer can inspect that context and see what the
|
|
10
|
+
# definition needs.
|
|
11
|
+
class UndefinedLet < NoMethodError; end
|
|
12
|
+
|
|
13
|
+
autoload :Initializer, 'matsuri/initializer'
|
|
14
|
+
autoload :Config, 'matsuri/config'
|
|
15
|
+
autoload :Platform, 'matsuri/platform'
|
|
16
|
+
autoload :ShellOut, 'matsuri/shell_out'
|
|
17
|
+
autoload :Registry, 'matsuri/registry'
|
|
18
|
+
|
|
19
|
+
autoload :Task, 'matsuri/task'
|
|
20
|
+
autoload :Cmd, 'matsuri/cmd'
|
|
21
|
+
autoload :Stack, 'matsuri/stack'
|
|
22
|
+
autoload :Acceptor, 'matsuri/acceptor'
|
|
23
|
+
autoload :Presenter, 'matsuri/presenter'
|
|
24
|
+
autoload :Transform, 'matsuri/transform'
|
|
25
|
+
|
|
26
|
+
module Acceptors
|
|
27
|
+
autoload :ImageTag, 'matsuri/acceptors/image_tag'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module Presenters
|
|
31
|
+
autoload :SeedImage, 'matsuri/presenters/seed_image'
|
|
32
|
+
autoload :SeedReplicas, 'matsuri/presenters/seed_replicas'
|
|
33
|
+
autoload :SeedResources, 'matsuri/presenters/seed_resources'
|
|
34
|
+
autoload :PruneEmpty, 'matsuri/presenters/prune_empty'
|
|
35
|
+
autoload :Helpers, 'matsuri/presenters/helpers'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Kubernetes
|
|
39
|
+
autoload :Base, 'matsuri/kubernetes/base'
|
|
40
|
+
|
|
41
|
+
# Core
|
|
42
|
+
autoload :Pod, 'matsuri/kubernetes/pod'
|
|
43
|
+
autoload :Service, 'matsuri/kubernetes/service'
|
|
44
|
+
autoload :Endpoints, 'matsuri/kubernetes/endpoints'
|
|
45
|
+
autoload :Ingress, 'matsuri/kubernetes/ingress'
|
|
46
|
+
autoload :Secret, 'matsuri/kubernetes/secret'
|
|
47
|
+
autoload :ConfigMap, 'matsuri/kubernetes/config_map'
|
|
48
|
+
|
|
49
|
+
# Apps
|
|
50
|
+
autoload :ReplicaSet, 'matsuri/kubernetes/replica_set'
|
|
51
|
+
autoload :StatefulSet, 'matsuri/kubernetes/stateful_set'
|
|
52
|
+
autoload :DaemonSet, 'matsuri/kubernetes/daemon_set'
|
|
53
|
+
autoload :Deployment, 'matsuri/kubernetes/deployment'
|
|
54
|
+
autoload :CronJob, 'matsuri/kubernetes/cron_job'
|
|
55
|
+
|
|
56
|
+
autoload :HorizontalPodAutoscaler, 'matsuri/kubernetes/horizontal_pod_autoscaler'
|
|
57
|
+
|
|
58
|
+
# Persistent Storage
|
|
59
|
+
autoload :PersistentVolume, 'matsuri/kubernetes/persistent_volume'
|
|
60
|
+
autoload :PersistentVolumeClaim, 'matsuri/kubernetes/persistent_volume_claim'
|
|
61
|
+
autoload :StorageClass, 'matsuri/kubernetes/storage_class'
|
|
62
|
+
|
|
63
|
+
# Other
|
|
64
|
+
autoload :ImportedManifest, 'matsuri/kubernetes/imported_manifest'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
module Cmds
|
|
68
|
+
autoload :Cli, 'matsuri/cmds/cli'
|
|
69
|
+
autoload :Kubectl, 'matsuri/cmds/kubectl'
|
|
70
|
+
autoload :Show, 'matsuri/cmds/show'
|
|
71
|
+
autoload :Diff, 'matsuri/cmds/diff'
|
|
72
|
+
autoload :Status, 'matsuri/cmds/status'
|
|
73
|
+
autoload :Create, 'matsuri/cmds/create'
|
|
74
|
+
autoload :Delete, 'matsuri/cmds/delete'
|
|
75
|
+
autoload :Apply, 'matsuri/cmds/apply'
|
|
76
|
+
autoload :Recreate, 'matsuri/cmds/recreate'
|
|
77
|
+
autoload :Scale, 'matsuri/cmds/scale'
|
|
78
|
+
|
|
79
|
+
autoload :Generate, 'matsuri/cmds/generate'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
module Tasks
|
|
83
|
+
autoload :Kubectl, 'matsuri/tasks/kubectl'
|
|
84
|
+
autoload :KubectlOps, 'matsuri/tasks/kubectl_ops'
|
|
85
|
+
autoload :Stacks, 'matsuri/tasks/stacks'
|
|
86
|
+
autoload :HashDiff, 'matsuri/tasks/hash_diff'
|
|
87
|
+
autoload :Docker, 'matsuri/tasks/docker'
|
|
88
|
+
autoload :Pod, 'matsuri/tasks/pod'
|
|
89
|
+
autoload :Cluster, 'matsuri/tasks/cluster'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
module Concerns
|
|
93
|
+
autoload :ImportManifest, 'matsuri/concerns/import_manifest'
|
|
94
|
+
autoload :ExportManifest, 'matsuri/concerns/export_manifest'
|
|
95
|
+
autoload :Diffable, 'matsuri/concerns/diffable'
|
|
96
|
+
|
|
97
|
+
autoload :Awaiting, 'matsuri/concerns/awaiting'
|
|
98
|
+
autoload :RegistryHelpers, 'matsuri/concerns/registry_helpers'
|
|
99
|
+
autoload :PodTemplate, 'matsuri/concerns/pod_template'
|
|
100
|
+
|
|
101
|
+
module Pods
|
|
102
|
+
autoload :SpreadPods, 'matsuri/concerns/pods/spread_pods'
|
|
103
|
+
autoload :SpreadTopology, 'matsuri/concerns/pods/spread_topology'
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
module DSL
|
|
108
|
+
module Cluster
|
|
109
|
+
autoload :Scope, 'matsuri/dsl/cluster/scope'
|
|
110
|
+
|
|
111
|
+
autoload :Role, 'matsuri/dsl/cluster/role'
|
|
112
|
+
autoload :ClusterRole, 'matsuri/dsl/cluster/cluster_role'
|
|
113
|
+
autoload :AggregatedClusterRole, 'matsuri/dsl/cluster/aggregated_cluster_role'
|
|
114
|
+
autoload :Binding, 'matsuri/dsl/cluster/binding'
|
|
115
|
+
|
|
116
|
+
autoload :ServiceAccount, 'matsuri/dsl/cluster/service_account'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
module Concerns
|
|
120
|
+
autoload :Metadata, 'matsuri/dsl/concerns/metadata'
|
|
121
|
+
autoload :RbacRules, 'matsuri/dsl/concerns/rbac_rules'
|
|
122
|
+
autoload :DefaultBinding, 'matsuri/dsl/concerns/default_binding'
|
|
123
|
+
autoload :ManifestSet, 'matsuri/dsl/concerns/manifest_set'
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def self.define(type, name, inherits: nil, &blk)
|
|
128
|
+
Matsuri::Registry.define(type, name, inherits: inherits, &blk)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.environment
|
|
132
|
+
Matsuri::Config.environment
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.dev?
|
|
136
|
+
Matsuri::Config.environment == 'dev'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.staging?
|
|
140
|
+
Matsuri::Config.environment == 'staging'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.production?
|
|
144
|
+
Matsuri::Config.environment == 'production'
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.log(level, message = nil, &blk)
|
|
148
|
+
case level
|
|
149
|
+
when :fatal then
|
|
150
|
+
log_output! message, &blk
|
|
151
|
+
exit(1)
|
|
152
|
+
when :error, :warn then log_output! message, &blk
|
|
153
|
+
when :info then log_output! message, &blk if Matsuri::Config.verbose
|
|
154
|
+
when :debug then log_output! message, &blk if Matsuri::Config.debug
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.log_output!(message)
|
|
159
|
+
puts(message || yield)
|
|
160
|
+
end
|
|
5
161
|
end
|
data/matsuri.gemspec
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
5
|
require 'matsuri/version'
|
|
@@ -7,18 +8,30 @@ Gem::Specification.new do |spec|
|
|
|
7
8
|
spec.name = "matsuri"
|
|
8
9
|
spec.version = Matsuri::VERSION
|
|
9
10
|
spec.authors = ["Ho-Sheng Hsiao"]
|
|
10
|
-
spec.email = ["
|
|
11
|
+
spec.email = ["ho-sheng.hsiao@solace.health"]
|
|
11
12
|
|
|
12
13
|
spec.summary = %q{Build a dev environment using Docker and Kubernetes}
|
|
13
14
|
spec.description = %q{Framework and toolkit to build a dev environment using Docker and Kubernetes}
|
|
14
|
-
spec.homepage = "https://github.com/
|
|
15
|
+
spec.homepage = "https://github.com/matsuri-rb/matsuri"
|
|
15
16
|
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
-
spec.bindir = "
|
|
18
|
-
spec.executables = spec.files.grep(%r{^
|
|
18
|
+
spec.bindir = "bin"
|
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
19
20
|
spec.require_paths = ["lib"]
|
|
20
21
|
|
|
21
|
-
spec.
|
|
22
|
-
|
|
22
|
+
spec.required_ruby_version = ">= 3.2"
|
|
23
|
+
|
|
24
|
+
spec.add_dependency 'base64'
|
|
25
|
+
spec.add_dependency 'thor'
|
|
26
|
+
spec.add_dependency 'mixlib-shellout'
|
|
27
|
+
spec.add_dependency 'mixlib-config'
|
|
28
|
+
spec.add_dependency 'rlet', '~> 0.7.0'
|
|
29
|
+
spec.add_dependency 'map'
|
|
30
|
+
spec.add_dependency 'hashdiff'
|
|
31
|
+
spec.add_dependency 'rainbow'
|
|
32
|
+
spec.add_dependency 'unit', '~> 0.5.0'
|
|
33
|
+
|
|
34
|
+
spec.add_development_dependency "bundler", ">= 2.4"
|
|
35
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
23
36
|
spec.add_development_dependency "rspec"
|
|
24
37
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Starts the matsuri test cluster under its own profile. --keep-context
|
|
5
|
+
# leaves the user's kubectl context alone; scripts address the cluster
|
|
6
|
+
# with --context matsuri. Pass minikube flags through, e.g.
|
|
7
|
+
# bin/minikube-start.sh --kubernetes-version=v1.34.0.
|
|
8
|
+
# kubectl and minikube dial the VM over the local network: on macOS, run
|
|
9
|
+
# from a Local Network-granted context (not tmux).
|
|
10
|
+
PROFILE="${PROFILE:-matsuri}"
|
|
11
|
+
|
|
12
|
+
# vfkit is the darwin driver; the flake only ships it there.
|
|
13
|
+
case "$(uname -s)" in
|
|
14
|
+
Darwin) DRIVER="${DRIVER:-vfkit}" ;;
|
|
15
|
+
*) DRIVER="${DRIVER:-podman}" ;;
|
|
16
|
+
esac
|
|
17
|
+
|
|
18
|
+
minikube start \
|
|
19
|
+
-p "${PROFILE}" \
|
|
20
|
+
--driver="${DRIVER}" \
|
|
21
|
+
--keep-context \
|
|
22
|
+
"$@"
|
|
23
|
+
|
|
24
|
+
# The example repo's namespaces precede anything matsuri converges;
|
|
25
|
+
# they are raw manifests, applied outside matsuri.
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
kubectl --context "${PROFILE}" apply -f "${SCRIPT_DIR}/../example/platform/namespaces"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Probes the mechanics the seed model rests on: a create WITHOUT
|
|
5
|
+
# --save-config (the kubectl default) keeps seeded fields out of
|
|
6
|
+
# last-applied, so ongoing applies of a manifest that omits them never
|
|
7
|
+
# prune them, and another controller (an HPA) takes ownership
|
|
8
|
+
# uncontested.
|
|
9
|
+
#
|
|
10
|
+
# Sequence:
|
|
11
|
+
# 1. create from the seeded manifest (replicas: 3), no --save-config
|
|
12
|
+
# 2. apply the declared manifest (no replicas) -> replicas must stay 3
|
|
13
|
+
# 3. scale to 5 (stands in for the HPA taking ownership)
|
|
14
|
+
# 4. apply the declared manifest again -> replicas must stay 5
|
|
15
|
+
#
|
|
16
|
+
# Start the cluster with bin/minikube-start.sh first. kubectl dials the
|
|
17
|
+
# VM: on macOS run from a Local Network-granted context (not tmux).
|
|
18
|
+
|
|
19
|
+
TESTS_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
20
|
+
CONTEXT="${CONTEXT:-matsuri}"
|
|
21
|
+
K8S="${TESTS_DIR}/k8s/save-config"
|
|
22
|
+
KUBECTL=(kubectl --context "${CONTEXT}" --namespace matsuri-probes)
|
|
23
|
+
|
|
24
|
+
fail() { echo "FAIL: $*" >&2; exit 1; }
|
|
25
|
+
|
|
26
|
+
replicas() {
|
|
27
|
+
"${KUBECTL[@]}" get deployment save-config-probe -o jsonpath='{.spec.replicas}'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
kubectl --context "${CONTEXT}" apply -f "${K8S}/namespace.yaml"
|
|
31
|
+
"${KUBECTL[@]}" delete deployment save-config-probe --ignore-not-found --wait
|
|
32
|
+
|
|
33
|
+
"${KUBECTL[@]}" create -f "${K8S}/deployment-seeded.yaml"
|
|
34
|
+
[ "$(replicas)" = "3" ] || fail "create did not land the seed (replicas=$(replicas))"
|
|
35
|
+
|
|
36
|
+
"${KUBECTL[@]}" apply -f "${K8S}/deployment-declared.yaml"
|
|
37
|
+
[ "$(replicas)" = "3" ] || fail "first apply pruned the seeded replicas (replicas=$(replicas))"
|
|
38
|
+
|
|
39
|
+
"${KUBECTL[@]}" scale deployment save-config-probe --replicas=5
|
|
40
|
+
"${KUBECTL[@]}" apply -f "${K8S}/deployment-declared.yaml"
|
|
41
|
+
[ "$(replicas)" = "5" ] || fail "apply clobbered controller-owned replicas (replicas=$(replicas))"
|
|
42
|
+
|
|
43
|
+
echo "last-applied for the record (replicas must be absent):"
|
|
44
|
+
"${KUBECTL[@]}" apply view-last-applied deployment save-config-probe
|
|
45
|
+
|
|
46
|
+
echo "PASS: seeded create + ongoing applies leave replicas controller-owned"
|