1 package kclient
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "strings"
9
10 "k8s.io/apimachinery/pkg/api/meta"
11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12 "k8s.io/apimachinery/pkg/runtime"
13 "k8s.io/apimachinery/pkg/runtime/schema"
14
15 "github.com/go-openapi/spec"
16 olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
17 kerrors "k8s.io/apimachinery/pkg/api/errors"
18 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19 "k8s.io/klog"
20 )
21
22
23 func (c *Client) IsCSVSupported() (bool, error) {
24 return c.IsResourceSupported("operators.coreos.com", "v1alpha1", "clusterserviceversions")
25 }
26
27
28
29 func (c *Client) ListClusterServiceVersions() (*olm.ClusterServiceVersionList, error) {
30 klog.V(3).Infof("Fetching list of operators installed in cluster")
31 csvs, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).List(context.TODO(), v1.ListOptions{})
32 if err != nil {
33 if kerrors.IsNotFound(err) {
34 return &olm.ClusterServiceVersionList{}, nil
35 }
36 return &olm.ClusterServiceVersionList{}, err
37 }
38 return csvs, nil
39 }
40
41
42 func (c *Client) GetCustomResourcesFromCSV(csv *olm.ClusterServiceVersion) *[]olm.CRDDescription {
43
44 return &csv.Spec.CustomResourceDefinitions.Owned
45 }
46
47
48 func (c *Client) GetCSVWithCR(name string) (*olm.ClusterServiceVersion, error) {
49 csvs, err := c.ListClusterServiceVersions()
50 if err != nil {
51 return &olm.ClusterServiceVersion{}, fmt.Errorf("unable to list services: %w", err)
52 }
53
54 for _, csv := range csvs.Items {
55 clusterServiceVersion := csv
56 for _, cr := range *c.GetCustomResourcesFromCSV(&clusterServiceVersion) {
57 if cr.Kind == name {
58 return &csv, nil
59 }
60 }
61 }
62 return &olm.ClusterServiceVersion{}, fmt.Errorf("could not find any Operator containing requested CR: %s", name)
63 }
64
65
66 func (c *Client) GetResourceSpecDefinition(group, version, kind string) (*spec.Schema, error) {
67 data, err := c.KubeClient.Discovery().RESTClient().Get().AbsPath("/openapi/v2").SetHeader("Accept", "application/json").Do(context.TODO()).Raw()
68 if err != nil {
69 return nil, err
70 }
71 return getResourceSpecDefinitionFromSwagger(data, group, version, kind)
72 }
73
74
75 func getResourceSpecDefinitionFromSwagger(data []byte, group, version, kind string) (*spec.Schema, error) {
76 schema := new(spec.Schema)
77 err := json.Unmarshal(data, schema)
78 if err != nil {
79 return nil, err
80 }
81
82 var crd spec.Schema
83 found := false
84 loopDefinitions:
85 for _, definition := range schema.Definitions {
86 extensions := definition.Extensions
87 gvkI, ok := extensions["x-kubernetes-group-version-kind"]
88 if !ok {
89 continue
90 }
91
92
93 gvkA, ok := gvkI.([]interface{})
94 if !ok {
95 continue
96 }
97
98 for i := range gvkA {
99
100
101 gvk, ok := gvkA[i].(map[string]interface{})
102 if !ok {
103 continue
104 }
105 gvkGroup := gvk["group"].(string)
106 gvkVersion := gvk["version"].(string)
107 gvkKind := gvk["kind"].(string)
108 if strings.HasSuffix(group, gvkGroup) && version == gvkVersion && kind == gvkKind {
109 crd = definition
110 found = true
111 break loopDefinitions
112 }
113 }
114
115 }
116 if !found {
117 return nil, errors.New("no definition found")
118 }
119
120 spec, ok := crd.Properties["spec"]
121 if ok {
122 return &spec, nil
123 }
124 return nil, nil
125 }
126
127
128 func toOpenAPISpec(repr *olm.CRDDescription) *spec.Schema {
129 if len(repr.SpecDescriptors) == 0 {
130 return nil
131 }
132 schema := new(spec.Schema).Typed("object", "")
133 schema.AdditionalProperties = &spec.SchemaOrBool{
134 Allows: false,
135 }
136 for _, param := range repr.SpecDescriptors {
137 addParam(schema, param)
138 }
139 return schema
140 }
141
142
143 func addParam(schema *spec.Schema, param olm.SpecDescriptor) {
144 parts := strings.SplitN(param.Path, ".", 2)
145 if len(parts) == 1 {
146 child := spec.StringProperty()
147 if len(param.XDescriptors) == 1 {
148 switch param.XDescriptors[0] {
149 case "urn:alm:descriptor:com.tectonic.ui:podCount":
150 child = spec.Int32Property()
151
152
153
154 }
155 }
156 child = child.WithTitle(param.DisplayName).WithDescription(param.Description)
157 schema.SetProperty(parts[0], *child)
158 } else {
159 var child *spec.Schema
160 if _, ok := schema.Properties[parts[0]]; ok {
161 c := schema.Properties[parts[0]]
162 child = &c
163 } else {
164 child = new(spec.Schema).Typed("object", "")
165 }
166 param.Path = parts[1]
167 addParam(child, param)
168 schema.SetProperty(parts[0], *child)
169 }
170 }
171
172
173 func (c *Client) GetRestMappingFromUnstructured(u unstructured.Unstructured) (*meta.RESTMapping, error) {
174 gvk := u.GroupVersionKind()
175 return c.restmapper.RESTMapping(gvk.GroupKind(), gvk.Version)
176 }
177
178 func (c *Client) GetRestMappingFromGVK(gvk schema.GroupVersionKind) (*meta.RESTMapping, error) {
179 return c.restmapper.RESTMapping(gvk.GroupKind(), gvk.Version)
180 }
181
182 func (c *Client) GetGVKFromGVR(gvr schema.GroupVersionResource) (schema.GroupVersionKind, error) {
183 return c.restmapper.KindFor(gvr)
184 }
185
186 func (c *Client) GetGVRFromGVK(gvk schema.GroupVersionKind) (schema.GroupVersionResource, error) {
187 mapping, err := c.restmapper.RESTMapping(gvk.GroupKind())
188 if err != nil {
189 return schema.GroupVersionResource{}, err
190 }
191 return mapping.Resource, nil
192 }
193
194
195 func (c *Client) GetOperatorGVRList() ([]meta.RESTMapping, error) {
196 var operatorGVRList []meta.RESTMapping
197
198
199 csvs, err := c.ListClusterServiceVersions()
200 if err != nil {
201 return operatorGVRList, err
202 }
203 for _, c := range csvs.Items {
204 owned := c.Spec.CustomResourceDefinitions.Owned
205 for i := range owned {
206 operatorGVRList = append(operatorGVRList, meta.RESTMapping{
207 Resource: GetGVRFromCR(&owned[i]),
208 })
209 }
210 }
211 return operatorGVRList, nil
212 }
213
214 func ConvertUnstructuredToResource(u unstructured.Unstructured, obj interface{}) error {
215 return runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), obj)
216 }
217
218 func ConvertUnstructuredListToResource(u unstructured.UnstructuredList, obj interface{}) error {
219 return runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), obj)
220 }
221
View as plain text