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