1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package servicemappingloader
16
17 import (
18 "fmt"
19 "sort"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/config/servicemappings"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/gcp"
24 autogenloader "github.com/GoogleCloudPlatform/k8s-config-connector/scripts/resource-autogen/servicemapping/servicemappingloader"
25
26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27 "k8s.io/apimachinery/pkg/runtime/schema"
28 "sigs.k8s.io/yaml"
29 )
30
31 type ServiceMappingLoader struct {
32 groupToSM map[string]v1alpha1.ServiceMapping
33 serviceHostNameToSM map[string]v1alpha1.ServiceMapping
34 }
35
36 func New() (*ServiceMappingLoader, error) {
37 serviceMappings, err := GetServiceMappings()
38 if err != nil {
39 return nil, fmt.Errorf("error loading service mappings: %v", err)
40 }
41 return NewFromServiceMappings(serviceMappings), nil
42 }
43
44 func NewFromServiceMappings(serviceMappings []v1alpha1.ServiceMapping) *ServiceMappingLoader {
45 groupToSM := make(map[string]v1alpha1.ServiceMapping)
46 serviceHostNameToSM := make(map[string]v1alpha1.ServiceMapping)
47 for _, sm := range serviceMappings {
48 groupToSM[sm.Name] = sm
49 serviceHostNameToSM[sm.Spec.ServiceHostName] = sm
50 }
51 loader := ServiceMappingLoader{
52 groupToSM: groupToSM,
53 serviceHostNameToSM: serviceHostNameToSM,
54 }
55 return &loader
56 }
57
58 func (s *ServiceMappingLoader) GetServiceMapping(name string) (*v1alpha1.ServiceMapping, error) {
59 sm, ok := s.groupToSM[name]
60 if !ok {
61 return nil, fmt.Errorf("unable to get service mapping: no mapping with name '%v' found", name)
62 }
63 return &sm, nil
64 }
65
66 func (s *ServiceMappingLoader) GetServiceMappingForServiceHostName(hostName string) (*v1alpha1.ServiceMapping, error) {
67 sm, ok := s.serviceHostNameToSM[hostName]
68 if !ok {
69 return nil, fmt.Errorf("unable to get service mapping: no mapping with service host name '%v' found", hostName)
70 }
71 return &sm, nil
72 }
73
74 func (s *ServiceMappingLoader) GetServiceMappings() []v1alpha1.ServiceMapping {
75 serviceMappings := make([]v1alpha1.ServiceMapping, 0, len(s.groupToSM))
76 for _, v := range s.groupToSM {
77 serviceMappings = append(serviceMappings, v)
78 }
79 return serviceMappings
80 }
81
82 func (s *ServiceMappingLoader) GetResourceConfig(u *unstructured.Unstructured) (*v1alpha1.ResourceConfig, error) {
83 sm, err := s.GetServiceMapping(u.GroupVersionKind().Group)
84 if err != nil {
85 return nil, fmt.Errorf("error getting service mapping: %v", err)
86 }
87 return GetResourceConfig(sm, u)
88 }
89
90
91 func (s *ServiceMappingLoader) GetResourceConfigs(gvk schema.GroupVersionKind) ([]*v1alpha1.ResourceConfig, error) {
92 sm, err := s.GetServiceMapping(gvk.Group)
93 if err != nil {
94 return nil, fmt.Errorf("error getting service mapping for group '%v': %v", gvk.Group, err)
95 }
96 return GetResourceConfigsForKind(sm, gvk.Kind), nil
97 }
98
99 func (s *ServiceMappingLoader) GetAutoGenOnlyGroups() map[string]bool {
100 autoGenOnlyServices := make(map[string]bool)
101 for group, sm := range s.groupToSM {
102 if len(sm.Spec.Resources) == 0 {
103 continue
104 }
105
106 hasManualResources := false
107 for _, rc := range sm.Spec.Resources {
108 if !rc.AutoGenerated {
109 hasManualResources = true
110 break
111 }
112 }
113 if !hasManualResources {
114 autoGenOnlyServices[group] = true
115 }
116 }
117 return autoGenOnlyServices
118 }
119
120 func GetResourceConfig(sm *v1alpha1.ServiceMapping, u *unstructured.Unstructured) (*v1alpha1.ResourceConfig, error) {
121 rcs := GetResourceConfigsForKind(sm, u.GetKind())
122 if len(rcs) == 0 {
123 return nil, fmt.Errorf("couldn't find any ResourceConfig defined for kind %v", u.GetKind())
124 }
125 if len(rcs) == 1 {
126 return rcs[0], nil
127 }
128
129
130 if u.GetKind() == "ComputeInstance" {
131 return getComputeInstanceTFResource(u, rcs)
132 }
133
134 l, err := getLocationalityOfResource(u)
135 if err != nil {
136 return nil, fmt.Errorf("couldn't find the right ResourceConfig for Kind %v: %v", u.GetKind(), err)
137 }
138 for _, rc := range rcs {
139 if rc.Locationality == l {
140 return rc, nil
141 }
142 }
143 return nil, fmt.Errorf("couldn't find the right ResourceConfig for Kind %v given the locationality of the resource - %v: %v", u.GetKind(), l, err)
144 }
145
146 func GetResourceConfigsForKind(sm *v1alpha1.ServiceMapping, kind string) []*v1alpha1.ResourceConfig {
147 rcs := make([]*v1alpha1.ResourceConfig, 0)
148 for _, r := range sm.Spec.Resources {
149 r := r
150 if r.Kind == kind {
151 rcs = append(rcs, &r)
152 }
153 }
154 return rcs
155 }
156
157 func GetResourceConfigsForTFType(sm *v1alpha1.ServiceMapping, tfType string) (*v1alpha1.ResourceConfig, error) {
158 for _, r := range sm.Spec.Resources {
159 r := r
160 if r.Name == tfType {
161 return &r, nil
162 }
163 }
164 return nil, fmt.Errorf("can't find resource config for TF type '%v' in group '%v'", tfType, sm.Name)
165 }
166
167 func getLocationalityOfResource(u *unstructured.Unstructured) (string, error) {
168 location, found, err := unstructured.NestedString(u.Object, "spec", "location")
169 if err != nil || !found {
170 return "", fmt.Errorf("couldn't find location field: %v", err)
171 }
172
173 if location == gcp.Global {
174 return gcp.Global, nil
175 }
176
177 if gcp.IsLocationRegional(location) {
178 return gcp.Regional, nil
179 }
180
181 if gcp.IsLocationZonal(location) {
182 return gcp.Zonal, nil
183 }
184 return "", fmt.Errorf("location is neither global nor regional nor zonal")
185 }
186
187 func GetServiceMappings() ([]v1alpha1.ServiceMapping, error) {
188 keys, err := servicemappings.AllKeys()
189 if err != nil {
190 return nil, fmt.Errorf("error listing servicemappings: %w", err)
191 }
192
193 autoGenSMMap, err := autogenloader.GetServiceMappingMap()
194 if err != nil {
195 return nil, fmt.Errorf("error getting auto-generated service mappings: %w", err)
196 }
197
198 serviceMappings := make([]v1alpha1.ServiceMapping, 0)
199 for _, key := range keys {
200 sm, err := fileToServiceMapping(key)
201 if err != nil {
202 return nil, err
203 }
204
205
206
207 if autoGenSM, ok := autoGenSMMap[sm.Name]; ok {
208 rcMap := make(map[string]bool)
209 for _, rc := range sm.Spec.Resources {
210 rcMap[rc.Name] = true
211 }
212
213 for _, rc := range autoGenSM.Spec.Resources {
214 if _, ok := rcMap[rc.Name]; !ok {
215 sm.Spec.Resources = append(sm.Spec.Resources, rc)
216 }
217 }
218
219 sort.Slice(sm.Spec.Resources, func(i, j int) bool {
220 return sm.Spec.Resources[i].Name < sm.Spec.Resources[j].Name
221 })
222
223 delete(autoGenSMMap, sm.Name)
224 }
225
226 serviceMappings = append(serviceMappings, *sm)
227 }
228
229 for _, autoGenSM := range autoGenSMMap {
230 serviceMappings = append(serviceMappings, autoGenSM)
231 }
232
233 return serviceMappings, nil
234 }
235
236 func fileToServiceMapping(key string) (*v1alpha1.ServiceMapping, error) {
237 b, err := servicemappings.ServiceMapping(key)
238 if err != nil {
239 return nil, fmt.Errorf("error reading servicemapping %q: %w", key, err)
240 }
241 sm, err := parseServiceMapping(b)
242 if err != nil {
243 return nil, fmt.Errorf("error parsing %q to service mapping: %w", key, err)
244 }
245 return sm, nil
246 }
247
248 func parseServiceMapping(b []byte) (*v1alpha1.ServiceMapping, error) {
249 var sm v1alpha1.ServiceMapping
250 if err := yaml.UnmarshalStrict(b, &sm); err != nil {
251 return nil, fmt.Errorf("error unmarshaling byte to service mapping: %w", err)
252 }
253 return &sm, nil
254 }
255
256 func getComputeInstanceTFResource(u *unstructured.Unstructured, rcs []*v1alpha1.ResourceConfig) (*v1alpha1.ResourceConfig, error) {
257
258
259 _, hasTemplate, err := unstructured.NestedMap(u.Object, "spec", "instanceTemplateRef")
260 if err != nil {
261 return nil, fmt.Errorf("instanceTemplateRef should be a map in Kind %v: %v", u.GetKind(), err)
262 }
263 for _, rc := range rcs {
264 if rc.Name == "google_compute_instance_from_template" && hasTemplate {
265 return rc, nil
266 }
267 if rc.Name == "google_compute_instance" && !hasTemplate {
268 return rc, nil
269 }
270 }
271
272 return nil, fmt.Errorf("couldn't find the right ResourceConfig for Kind %v: %v", u.GetKind(), err)
273 }
274
View as plain text