-
Notifications
You must be signed in to change notification settings - Fork 0
/
macros.cr
193 lines (171 loc) · 6.5 KB
/
macros.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
require "json"
require "yaml"
require "./error"
macro k8s_sanitize_api(api)
{{api}}.gsub(/(\.authorization)?\.k8s\.io/, "")
end
macro k8s_resource_class(group, ver, kind)
{% others = {} of StringLiteral => TypeNode %}
{% for resource in K8S::Kubernetes::Resource.all_subclasses %}
{% if !resource.abstract? && resource.annotation(::K8S::GroupVersionKind) %}
{% anno = resource.annotation(::K8S::GroupVersionKind) %}
{% value = "{#{anno[:group].gsub(/(\.authorization)?\.k8s\.io/, "")},#{anno[:version]},#{anno[:kind]}}" %}
{% if !others[value] %} {% others[value] = resource %} {% end %}
{% end %}
{% end %}
{% for resource in K8S::Kubernetes::Resource.all_subclasses %}
{% if !resource.abstract? && resource.annotation(::K8S::GroupVersionKind) %}
{% anno = resource.annotation(::K8S::GroupVersionKind) %}
{% value = "{\"\",#{anno[:version]},#{anno[:kind]}}" %}
{% if anno[:group] != "" && !others[value] %} {% others[value] = resource %} {% end %}
{% value1 = "{\"core\",#{anno[:version]},#{anno[:kind]}}" %}
{% if !others[value1] && anno[:group] == "" %} {% others[value1] = resource %} {% end %}
{% end %}
{% end %}
{% for mapping in K8S::Kubernetes::Resource::MAPPINGS %}
{% value = %<{"",#{mapping[0]},#{mapping[1].split("::").last}}> %}
{% if !others[value] %} {% others[value] = mapping[2].resolve %} {% end %}
{% if mapping[0] =~ /\// %}{% split = mapping[0].split('/') %}
{% value1 = "{#{split.first},#{split.last},#{anno[:kind]}}" %}
{% if !others[value1] && anno[:group] == "" %} {% others[value1] = resource %} {% end %}
{% end %}
{% end %}
case { {{group}}, {{ver}}, {{kind}} }
{% for key, resource in others %}
when {{key.id}}
{{resource.id}}
{% end %}
else
Log.warn &.emit %<Unknown api resource: "#{{{group}}}/#{{{ver}}}/#{{{kind}}}">,
group: {{group}}, version: {{ver}}, kind: {{kind}}
raise K8S::Error::UnknownResource.new("#{{{group}}}/#{{{ver}}}/#{{{kind}}}")
end
end
macro k8s_json_discriminator(mappings)
{% verbatim do %}
macro finished
def self.new(pull : ::JSON::PullParser)
location = pull.location
api_value = nil
discriminator_value = nil
# Try to find the discriminator while also getting the raw
# string value of the parsed JSON, so then we can pass it
# to the final type.
json = String.build do |io|
JSON.build(io) do |builder|
builder.start_object
pull.read_object do |key|
if key == "apiVersion" || key == "groupVersion"
value_kind = pull.kind
case value_kind
when .string?
api_value = k8s_sanitize_api(pull.string_value)
else
raise ::JSON::SerializableError.new("JSON discriminator field 'apiVersion' has an invalid value type of #{value_kind.to_s}", to_s, nil, *location, nil)
end
builder.field(key, api_value)
pull.read_next
elsif key == "kind"
value_kind = pull.kind
case value_kind
when .string?
discriminator_value = pull.string_value
else
raise ::JSON::SerializableError.new("JSON discriminator field 'kind' has an invalid value type of #{value_kind.to_s}", to_s, nil, *location, nil)
end
builder.field(key, discriminator_value)
pull.read_next
else
builder.field(key) { pull.read_raw(builder) }
end
end
builder.end_object
end
end
unless api_value
raise ::JSON::SerializableError.new("Missing JSON discriminator field 'apiVersion'", to_s, nil, *location, nil)
end
unless discriminator_value
raise ::JSON::SerializableError.new("Missing JSON discriminator field 'kind'", to_s, nil, *location, nil)
end
if discriminator_value == "APIResourceList"
return ::K8S::Apimachinery::Apis::Meta::V1::APIResourceList.from_json(json)
end
parts = api_value.split('/')
ver = parts.pop
group = parts.join('/')
klass = k8s_resource_class(group, ver, discriminator_value)
case klass
{% for resource in K8S::Kubernetes::Resource.all_subclasses %}
{% if !resource.abstract? && resource.annotation(::K8S::GroupVersionKind) %}
when {{resource.id}}.class
{{resource.id}}.from_json(json)
{% end %}{% end %}
else
raise K8S::Error::UndefinedResource.new(klass)
end
end
end
{% end %}
end
macro k8s_yaml_discriminator(mappings)
{% verbatim do %}
macro finished
def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
api_value = nil
discriminator_value = nil
ctx.read_alias(node, \{{@type}}) do |obj|
return obj
end
unless node.is_a?(YAML::Nodes::Mapping)
node.raise "expected YAML mapping, not #{node.class}"
end
node.each do |key, value|
next unless key.is_a?(YAML::Nodes::Scalar) && value.is_a?(YAML::Nodes::Scalar)
next unless key.value == "kind" || key.value == "apiVersion"
discriminator_value = value.value if key.value == "kind"
api_value = value.value.gsub(".k8s.io", "") if key.value == "apiVersion" || key.value == "groupVersion"
end
node.raise "Missing YAML discriminator field 'kind'" if discriminator_value.nil?
node.raise "Missing YAML discriminator field 'apiVersion'" if api_value.nil?
# for the compilers benefit
discriminator_value = discriminator_value.not_nil!
api_value = api_value.not_nil!
parts = api_value.split('/')
ver = parts.pop
group = parts.join('/')
klass = k8s_resource_class(group, ver, discriminator_value)
case klass
{% for resource in K8S::Kubernetes::Resource.all_subclasses %}
{% if !resource.abstract? && resource.annotation(::K8S::GroupVersionKind) %}
when {{resource.id}}.class
{{resource.id}}.new(ctx, node)
{% end %}{% end %}
else
raise K8S::Error::UndefinedResource.new(klass)
end
end
end
{% end %}
end
# TODO: Would like to be able to create objects from named tuples, but
# that requires a lot of work.
#
# macro finished
# {% for obj in Object.all_subclasses %}
# {% if !obj.abstract? && obj.annotation(::K8S::Properties) %}
# class {{obj.id}}
# def initialize(**args)
# {% for anno in obj.annotations(::K8S::Properties) %}
# {% props = anno.named_args %}
# {% for name, prop in props %}
# if args[:{{name.id}}]
# @{{name.id}} = args[:{{name}}]
# end
# {% end %}
# {% end %}
# end
# end
# {% end %}
# {% end %}
# end