github.com/interconnectedcloud/qdr-operator@v0.0.0-20210826174505-576d2b33dac7/test/e2e/framework/dynclient.go (about) 1 package framework 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "regexp" 11 12 "k8s.io/apimachinery/pkg/api/meta" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/apimachinery/pkg/runtime/serializer/yaml" 17 yamlutil "k8s.io/apimachinery/pkg/util/yaml" 18 "k8s.io/client-go/dynamic" 19 clientset "k8s.io/client-go/kubernetes" 20 "k8s.io/client-go/restmapper" 21 ) 22 23 // CreateResourcesFromYAML creates all resources from the provided YAML file 24 // or URL using an initialized VanClient instance. 25 func CreateResourcesFromYAML(kubeClient clientset.Interface, dynClient dynamic.Interface, namespace, fileOrUrl string) error { 26 var yamlData []byte 27 var err error 28 29 // Load YAML from an http/https url or local file 30 isUrl, _ := regexp.Compile("http[s]*://") 31 if isUrl.MatchString(fileOrUrl) { 32 yamlData, err = readYAMLFromUrl(fileOrUrl) 33 if err != nil { 34 return err 35 } 36 } else { 37 // Read YAML file 38 yamlData, err = ioutil.ReadFile(fileOrUrl) 39 if err != nil { 40 return fmt.Errorf("error reading yaml file: %s", err) 41 } 42 } 43 44 // Creating a dynamic client 45 if err != nil { 46 return fmt.Errorf("error creating a dynamic k8s client: %s", err) 47 } 48 // Read YAML file removing all comments and blank lines 49 // otherwise yamlDecoder does not work 50 yamlBuffer, err := readYAMLIgnoringComments(yamlData) 51 if err != nil { 52 return err 53 } 54 55 yamlDecoder := yamlutil.NewYAMLOrJSONDecoder(bufio.NewReader(&yamlBuffer), 1024) 56 57 for { 58 // Decoding raw object from yaml 59 var rawObj runtime.RawExtension 60 if err = yamlDecoder.Decode(&rawObj); err != nil { 61 if err != io.EOF { 62 return fmt.Errorf("error decoding yaml: %s", err) 63 } 64 break 65 } 66 obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil) 67 // Converts unstructured object into a map[string]interface{} 68 unstructureMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) 69 if err != nil { 70 return fmt.Errorf("unable to convert to unstructured map: %s", err) 71 } 72 // Create a generic unstructured object from map 73 unstructuredObj := &unstructured.Unstructured{Object: unstructureMap} 74 // Getting API Group Resources using discovery client 75 gr, err := restmapper.GetAPIGroupResources(kubeClient.Discovery()) 76 if err != nil { 77 return fmt.Errorf("error getting APIGroupResources: %s", err) 78 } 79 // Unstructured object mapper for the provided group and kind 80 mapper := restmapper.NewDiscoveryRESTMapper(gr) 81 mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) 82 if err != nil { 83 return fmt.Errorf("error obtaining mapping for: %s - %s", gvk.GroupVersion().String(), err) 84 } 85 // Dynamic resource handler 86 var k8sResource dynamic.ResourceInterface 87 if mapping.Scope.Name() == meta.RESTScopeNameNamespace { 88 if unstructuredObj.GetNamespace() == "" { 89 unstructuredObj.SetNamespace(namespace) 90 } 91 k8sResource = dynClient.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace()) 92 } else { 93 k8sResource = dynClient.Resource(mapping.Resource) 94 } 95 // Creating the dynamic resource 96 _, err = k8sResource.Create(unstructuredObj, metav1.CreateOptions{}) 97 if err != nil { 98 return fmt.Errorf("error creating resource [group=%s - kind=%s] - %s", gvk.Group, gvk.Kind, err) 99 } 100 } 101 102 return nil 103 } 104 105 // readYAMLFromUrl returns the content for the provided url 106 func readYAMLFromUrl(url string) ([]byte, error) { 107 var yamlData []byte 108 // Load from URL if url provided 109 resp, err := http.Get(url) 110 if err != nil { 111 return nil, fmt.Errorf("error loading yaml from url [%s]: %s", url, err) 112 } 113 defer resp.Body.Close() 114 yamlData, err = ioutil.ReadAll(resp.Body) 115 if err != nil { 116 return nil, fmt.Errorf("error reading yaml from url [%s]: %s", url, err) 117 } 118 return yamlData, nil 119 } 120 121 // readYAMLIgnoringComments returns a bytes.Buffer that contains the 122 // content from the loaded yamlData removing all comments and empty lines 123 // that exists before the beginning of the yaml data. This is needed 124 // for the k8s yaml decoder to properly identify if content is a YAML 125 // or JSON. 126 func readYAMLIgnoringComments(yamlData []byte) (bytes.Buffer, error) { 127 var yamlNoComments bytes.Buffer 128 129 // We must strip all comments and blank lines from yaml file 130 // otherwise the k8s yaml decoder might fail 131 yamlBytesReader := bytes.NewReader(yamlData) 132 yamlBufReader := bufio.NewReader(yamlBytesReader) 133 yamlBufWriter := bufio.NewWriter(&yamlNoComments) 134 135 // Regexp to exclude empty lines and lines with comments only 136 // till beginning of yaml content (otherwise yaml decoder 137 // won't be able to identify whether it is JSON or YAML). 138 ignoreRegexp, _ := regexp.Compile("^\\s*(#|$)") 139 yamlStarted := false 140 for { 141 line, err := yamlBufReader.ReadString('\n') 142 if err == io.EOF { 143 break 144 } 145 if !yamlStarted && ignoreRegexp.MatchString(line) { 146 continue 147 } 148 yamlStarted = true 149 yamlBufWriter.WriteString(line) 150 } 151 yamlBufWriter.Flush() 152 return yamlNoComments, nil 153 } 154