| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package IO::K8s::Resource; |
|
2
|
|
|
|
|
|
|
# ABSTRACT: Base class for all Kubernetes resources |
|
3
|
|
|
|
|
|
|
our $VERSION = '1.008'; |
|
4
|
28
|
|
|
28
|
|
154861
|
use v5.10; |
|
|
28
|
|
|
|
|
108
|
|
|
5
|
28
|
|
|
28
|
|
4923
|
use Moo (); |
|
|
28
|
|
|
|
|
79591
|
|
|
|
28
|
|
|
|
|
560
|
|
|
6
|
28
|
|
|
28
|
|
11386
|
use Moo::Role (); |
|
|
28
|
|
|
|
|
243309
|
|
|
|
28
|
|
|
|
|
853
|
|
|
7
|
28
|
|
|
28
|
|
15602
|
use Import::Into; |
|
|
28
|
|
|
|
|
47461
|
|
|
|
28
|
|
|
|
|
1246
|
|
|
8
|
28
|
|
|
28
|
|
5345
|
use Package::Stash; |
|
|
28
|
|
|
|
|
61934
|
|
|
|
28
|
|
|
|
|
1250
|
|
|
9
|
28
|
|
|
28
|
|
17740
|
use Types::Standard qw( ArrayRef Bool HashRef InstanceOf Int Maybe Str ); |
|
|
28
|
|
|
|
|
3789922
|
|
|
|
28
|
|
|
|
|
370
|
|
|
10
|
28
|
|
|
28
|
|
130887
|
use IO::K8s::Types qw( IntOrStr Quantity Time ); |
|
|
28
|
|
|
|
|
240
|
|
|
|
28
|
|
|
|
|
411
|
|
|
11
|
28
|
|
|
28
|
|
133721
|
use Scalar::Util qw(blessed); |
|
|
28
|
|
|
|
|
66
|
|
|
|
28
|
|
|
|
|
34480
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# Registry: class -> attr -> { type, class, is_array, is_hash, is_bool, is_int } |
|
14
|
|
|
|
|
|
|
# Use 'our' to make it a proper package variable accessible via symbol table |
|
15
|
|
|
|
|
|
|
our %_attr_registry; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# Class name expansion map |
|
18
|
|
|
|
|
|
|
my %_class_prefix = ( |
|
19
|
|
|
|
|
|
|
'Core' => 'IO::K8s::Api::Core', |
|
20
|
|
|
|
|
|
|
'Apps' => 'IO::K8s::Api::Apps', |
|
21
|
|
|
|
|
|
|
'Batch' => 'IO::K8s::Api::Batch', |
|
22
|
|
|
|
|
|
|
'Networking' => 'IO::K8s::Api::Networking', |
|
23
|
|
|
|
|
|
|
'Rbac' => 'IO::K8s::Api::Rbac', |
|
24
|
|
|
|
|
|
|
'Storage' => 'IO::K8s::Api::Storage', |
|
25
|
|
|
|
|
|
|
'Policy' => 'IO::K8s::Api::Policy', |
|
26
|
|
|
|
|
|
|
'Autoscaling' => 'IO::K8s::Api::Autoscaling', |
|
27
|
|
|
|
|
|
|
'Admissionregistration' => 'IO::K8s::Api::Admissionregistration', |
|
28
|
|
|
|
|
|
|
'Coordination' => 'IO::K8s::Api::Coordination', |
|
29
|
|
|
|
|
|
|
'Discovery' => 'IO::K8s::Api::Discovery', |
|
30
|
|
|
|
|
|
|
'Events' => 'IO::K8s::Api::Events', |
|
31
|
|
|
|
|
|
|
'Flowcontrol' => 'IO::K8s::Api::Flowcontrol', |
|
32
|
|
|
|
|
|
|
'Node' => 'IO::K8s::Api::Node', |
|
33
|
|
|
|
|
|
|
'Scheduling' => 'IO::K8s::Api::Scheduling', |
|
34
|
|
|
|
|
|
|
'Certificates' => 'IO::K8s::Api::Certificates', |
|
35
|
|
|
|
|
|
|
'Authentication' => 'IO::K8s::Api::Authentication', |
|
36
|
|
|
|
|
|
|
'Authorization' => 'IO::K8s::Api::Authorization', |
|
37
|
|
|
|
|
|
|
'Resource' => 'IO::K8s::Api::Resource', |
|
38
|
|
|
|
|
|
|
'Storagemigration' => 'IO::K8s::Api::Storagemigration', |
|
39
|
|
|
|
|
|
|
'Meta' => 'IO::K8s::Apimachinery::Pkg::Apis::Meta', |
|
40
|
|
|
|
|
|
|
'Apiextensions' => 'IO::K8s::ApiextensionsApiserver::Pkg::Apis::Apiextensions', |
|
41
|
|
|
|
|
|
|
'KubeAggregator' => 'IO::K8s::KubeAggregator::Pkg::Apis::Apiregistration', |
|
42
|
|
|
|
|
|
|
); |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Type flag lookup table |
|
45
|
|
|
|
|
|
|
my %TYPE_FLAGS = ( |
|
46
|
|
|
|
|
|
|
Str => { is_str => 1 }, |
|
47
|
|
|
|
|
|
|
Int => { is_int => 1 }, |
|
48
|
|
|
|
|
|
|
Bool => { is_bool => 1 }, |
|
49
|
|
|
|
|
|
|
IntOrStr => { is_int_or_string => 1 }, |
|
50
|
|
|
|
|
|
|
Quantity => { is_quantity => 1 }, |
|
51
|
|
|
|
|
|
|
Time => { is_time => 1 }, |
|
52
|
|
|
|
|
|
|
); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# For string path: map type name to base Type::Tiny constraint |
|
55
|
|
|
|
|
|
|
# Custom K8s types (IntOrStr, Quantity, Time) fall back to Str |
|
56
|
|
|
|
|
|
|
my %STR_ISA_MAP = ( |
|
57
|
|
|
|
|
|
|
Str => Str, |
|
58
|
|
|
|
|
|
|
Int => Int, |
|
59
|
|
|
|
|
|
|
Bool => Bool, |
|
60
|
|
|
|
|
|
|
); |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub import { |
|
63
|
1076
|
|
|
1076
|
|
91413
|
my $class = shift; |
|
64
|
1076
|
|
|
|
|
4345
|
my $caller = caller; |
|
65
|
1076
|
|
|
|
|
13442
|
$class->_setup_class($caller); |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub _setup_class { |
|
69
|
1081
|
|
|
1081
|
|
3837
|
my ($class, $target) = @_; |
|
70
|
1081
|
|
|
|
|
11796
|
Moo->import::into($target); |
|
71
|
1081
|
|
|
|
|
897596
|
Types::Standard->import::into($target, qw( Str Int Bool )); |
|
72
|
1081
|
|
|
|
|
4297031
|
IO::K8s::Types->import::into($target, qw( IntOrStr Quantity Time )); |
|
73
|
1081
|
|
|
|
|
5777054
|
Moo::Role->apply_roles_to_package($target, 'IO::K8s::Role::Resource'); |
|
74
|
1081
|
|
|
|
|
4800277
|
my $stash = Package::Stash->new($target); |
|
75
|
1081
|
|
|
4905
|
|
125340
|
$stash->add_symbol('&k8s', sub { $class->_k8s($target, @_) }); |
|
|
4905
|
|
|
|
|
56770
|
|
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub _expand_class { |
|
79
|
1854
|
|
|
1854
|
|
4722
|
my ($short) = @_; |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# +FullClassName - strip + and use as-is |
|
82
|
1854
|
50
|
|
|
|
6486
|
return substr($short, 1) if $short =~ /^\+/; |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Already fully qualified? |
|
85
|
1854
|
50
|
|
|
|
5949
|
return $short if $short =~ /^IO::K8s::/; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Check for prefix match (e.g., Core::V1::Pod) |
|
88
|
1854
|
100
|
|
|
|
12578
|
if ($short =~ /^([A-Z][a-z]+)::/) { |
|
89
|
1850
|
|
|
|
|
7293
|
my $prefix = $1; |
|
90
|
1850
|
100
|
|
|
|
7763
|
if (my $expansion = $_class_prefix{$prefix}) { |
|
91
|
1846
|
|
|
|
|
33335
|
$short =~ s/^$prefix/$expansion/; |
|
92
|
1846
|
|
|
|
|
8876
|
return $short; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# Default: assume it's under IO::K8s::Api |
|
97
|
8
|
|
|
|
|
28
|
return "IO::K8s::Api::$short"; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _is_type_tiny { |
|
101
|
7748
|
|
|
7748
|
|
15731
|
my ($obj) = @_; |
|
102
|
7748
|
|
66
|
|
|
57002
|
return blessed($obj) && $obj->isa('Type::Tiny'); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Sanitize JSON field names into valid Perl identifiers for Moo attributes |
|
106
|
|
|
|
|
|
|
# $ref -> _ref, $schema -> _schema, x-kubernetes-foo -> x_kubernetes_foo |
|
107
|
|
|
|
|
|
|
sub _sanitize_attr_name { |
|
108
|
4905
|
|
|
4905
|
|
12488
|
my ($name) = @_; |
|
109
|
4905
|
100
|
|
|
|
32537
|
return $name unless $name =~ /[^a-zA-Z0-9_]/; |
|
110
|
9
|
|
|
|
|
38
|
(my $safe = $name) =~ s/^\$/_/; |
|
111
|
9
|
|
|
|
|
50
|
$safe =~ s/-/_/g; |
|
112
|
9
|
|
|
|
|
24
|
return $safe; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub _k8s { |
|
116
|
4905
|
|
|
4905
|
|
16817
|
my ($class, $caller, $name, $type_spec, $required_marker) = @_; |
|
117
|
|
|
|
|
|
|
|
|
118
|
4905
|
|
|
|
|
10650
|
my $json_key = $name; |
|
119
|
4905
|
|
|
|
|
16174
|
my $attr_name = _sanitize_attr_name($name); |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Ensure the registry entry exists |
|
122
|
4905
|
100
|
|
|
|
35629
|
$_attr_registry{$caller} = {} unless exists $_attr_registry{$caller}; |
|
123
|
|
|
|
|
|
|
|
|
124
|
4905
|
|
|
|
|
10060
|
my %info; |
|
125
|
|
|
|
|
|
|
my $isa; |
|
126
|
4905
|
100
|
66
|
|
|
21783
|
my $required = $required_marker && $required_marker eq 'required' ? 1 : 0; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Check for ! suffix on strings (legacy/alternative required syntax) |
|
129
|
4905
|
50
|
66
|
|
|
37475
|
if (!ref $type_spec && !_is_type_tiny($type_spec) && $type_spec =~ s/!$//) { |
|
|
|
50
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
$required = 1; |
|
131
|
|
|
|
|
|
|
} elsif (ref $type_spec eq 'ARRAY' && !_is_type_tiny($type_spec->[0]) && $type_spec->[0] =~ s/!$//) { |
|
132
|
0
|
|
|
|
|
0
|
$required = 1; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Handle Type::Tiny objects directly (Str, Int, Bool, IntOrStr, Quantity, Time) |
|
136
|
4905
|
100
|
|
|
|
18678
|
if (_is_type_tiny($type_spec)) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
137
|
2374
|
|
|
|
|
43419
|
my $flags = $TYPE_FLAGS{$type_spec->name}; |
|
138
|
2374
|
50
|
|
|
|
82474
|
if ($flags) { |
|
139
|
2374
|
|
|
|
|
9936
|
%info = %$flags; |
|
140
|
2374
|
100
|
|
|
|
14425
|
$isa = $required ? $type_spec : Maybe[$type_spec]; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
} elsif (!ref $type_spec) { |
|
143
|
1389
|
100
|
|
|
|
5439
|
if (my $flags = $TYPE_FLAGS{$type_spec}) { |
|
144
|
19
|
|
|
|
|
73
|
%info = %$flags; |
|
145
|
19
|
|
66
|
|
|
100
|
my $base = $STR_ISA_MAP{$type_spec} // Str; |
|
146
|
19
|
50
|
|
|
|
347
|
$isa = $required ? $base : Maybe[$base]; |
|
147
|
|
|
|
|
|
|
} else { |
|
148
|
1370
|
|
|
|
|
4789
|
my $full_class = _expand_class($type_spec); |
|
149
|
1370
|
|
|
|
|
4888
|
$info{is_object} = 1; |
|
150
|
1370
|
|
|
|
|
3598
|
$info{class} = $full_class; |
|
151
|
1370
|
100
|
|
|
|
9526
|
$isa = $required ? InstanceOf[$full_class] : Maybe[InstanceOf[$full_class]]; |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
} elsif (ref $type_spec eq 'ARRAY') { |
|
154
|
727
|
|
|
|
|
1870
|
my $inner = $type_spec->[0]; |
|
155
|
|
|
|
|
|
|
# Handle [Str] with Type::Tiny object |
|
156
|
727
|
100
|
|
|
|
1806
|
if (_is_type_tiny($inner)) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
157
|
248
|
|
|
|
|
3173
|
my $type_name = $inner->name; |
|
158
|
248
|
100
|
|
|
|
8262
|
if ($type_name eq 'Str') { |
|
|
|
50
|
|
|
|
|
|
|
159
|
243
|
|
|
|
|
861
|
$info{is_array_of_str} = 1; |
|
160
|
|
|
|
|
|
|
} elsif ($type_name eq 'Int') { |
|
161
|
5
|
|
|
|
|
23
|
$info{is_array_of_int} = 1; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
248
|
100
|
|
|
|
1931
|
$isa = $required ? ArrayRef[$inner] : Maybe[ArrayRef[$inner]]; |
|
164
|
|
|
|
|
|
|
} elsif ($inner eq 'Str') { |
|
165
|
1
|
|
|
|
|
3
|
$info{is_array_of_str} = 1; |
|
166
|
1
|
50
|
|
|
|
5
|
$isa = $required ? ArrayRef[Str] : Maybe[ArrayRef[Str]]; |
|
167
|
|
|
|
|
|
|
} elsif ($inner eq 'Int') { |
|
168
|
0
|
|
|
|
|
0
|
$info{is_array_of_int} = 1; |
|
169
|
0
|
0
|
|
|
|
0
|
$isa = $required ? ArrayRef[Int] : Maybe[ArrayRef[Int]]; |
|
170
|
|
|
|
|
|
|
} else { |
|
171
|
478
|
|
|
|
|
1670
|
my $full_class = _expand_class($inner); |
|
172
|
478
|
|
|
|
|
1733
|
$info{is_array_of_objects} = 1; |
|
173
|
478
|
|
|
|
|
1244
|
$info{class} = $full_class; |
|
174
|
478
|
100
|
|
|
|
4647
|
$isa = $required ? ArrayRef[InstanceOf[$full_class]] : Maybe[ArrayRef[InstanceOf[$full_class]]]; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
} elsif (ref $type_spec eq 'HASH') { |
|
177
|
415
|
|
|
|
|
1476
|
my ($inner) = keys %$type_spec; |
|
178
|
415
|
100
|
|
|
|
1359
|
if ($inner eq 'Str') { |
|
179
|
409
|
|
|
|
|
1168
|
$info{is_hash_of_str} = 1; |
|
180
|
|
|
|
|
|
|
# Use plain HashRef without inner constraint - K8s has nested hashes |
|
181
|
|
|
|
|
|
|
# in fields like fieldsV1, annotations, labels which can have any structure |
|
182
|
409
|
50
|
|
|
|
2389
|
$isa = $required ? HashRef : Maybe[HashRef]; |
|
183
|
|
|
|
|
|
|
} else { |
|
184
|
6
|
|
|
|
|
22
|
my $full_class = _expand_class($inner); |
|
185
|
6
|
|
|
|
|
20
|
$info{is_hash_of_objects} = 1; |
|
186
|
6
|
|
|
|
|
22
|
$info{class} = $full_class; |
|
187
|
6
|
50
|
|
|
|
75
|
$isa = $required ? HashRef[InstanceOf[$full_class]] : Maybe[HashRef[InstanceOf[$full_class]]]; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# Store json_key when it differs from the Perl attribute name |
|
193
|
4905
|
100
|
|
|
|
3269988
|
$info{json_key} = $json_key if $attr_name ne $json_key; |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Register - use hash slice to copy values, not reference |
|
196
|
4905
|
|
|
|
|
36354
|
$_attr_registry{$caller}{$attr_name} = { %info }; |
|
197
|
28
|
|
|
28
|
|
246
|
no strict 'refs'; |
|
|
28
|
|
|
|
|
54
|
|
|
|
28
|
|
|
|
|
7141
|
|
|
198
|
4905
|
|
|
|
|
11175
|
push @{"${caller}::_k8s_attributes"}, $attr_name; |
|
|
4905
|
|
|
|
|
40159
|
|
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Only create the attribute if it doesn't already exist (e.g., from a role) |
|
201
|
4905
|
100
|
|
|
|
83314
|
return if $caller->can($attr_name); |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# Call Moo's has — use init_arg to map JSON key to Perl-safe attribute name |
|
204
|
4584
|
|
|
|
|
21273
|
my $has = $caller->can('has'); |
|
205
|
4584
|
100
|
|
|
|
29492
|
$has->($attr_name, is => 'rw', isa => $isa, |
|
|
|
100
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
($required ? (required => 1) : ()), |
|
207
|
|
|
|
|
|
|
($attr_name ne $json_key ? (init_arg => $json_key) : ()), |
|
208
|
|
|
|
|
|
|
); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
1; |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
__END__ |