github.com/jaylevin/jenkins-library@v1.230.4/pkg/cnbutils/bindings/bindings.go (about)

     1  // Package bindings provides utility function to create buildpack bindings folder structures
     2  package bindings
     3  
     4  import (
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/SAP/jenkins-library/pkg/cnbutils"
    14  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    15  	"github.com/mitchellh/mapstructure"
    16  )
    17  
    18  type binding struct {
    19  	Type    string  `json:"type"`
    20  	Key     string  `json:"key"`
    21  	Content *string `json:"content,omitempty"`
    22  	File    *string `json:"file,omitempty"`
    23  	FromURL *string `json:"fromUrl,omitempty"`
    24  }
    25  
    26  // Return error if:
    27  // 1. Content is set + File or FromURL
    28  // 2. File is set + FromURL or Content
    29  // 3. FromURL is set + File or Content
    30  // 4. Everything is set
    31  func (b *binding) validate() error {
    32  	if !validName(b.Key) {
    33  		return fmt.Errorf("invalid key: '%s'", b.Key)
    34  	}
    35  
    36  	if b.Content == nil && b.File == nil && b.FromURL == nil {
    37  		return errors.New("one of 'file', 'content' or 'fromUrl' properties must be specified for binding")
    38  	}
    39  
    40  	onlyOneSet := (b.Content != nil && b.File == nil && b.FromURL == nil) ||
    41  		(b.Content == nil && b.File != nil && b.FromURL == nil) ||
    42  		(b.Content == nil && b.File == nil && b.FromURL != nil)
    43  
    44  	if !onlyOneSet {
    45  		return errors.New("only one of 'content', 'file' or 'fromUrl' can be set for a binding")
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  type bindings map[string]binding
    52  
    53  // ProcessBindings creates the given bindings in the platform directory
    54  func ProcessBindings(utils cnbutils.BuildUtils, httpClient piperhttp.Sender, platformPath string, bindings map[string]interface{}) error {
    55  
    56  	typedBindings, err := toTyped(bindings)
    57  	if err != nil {
    58  		return errors.Wrap(err, "failed to convert map to struct")
    59  	}
    60  
    61  	for name, binding := range typedBindings {
    62  		err = processBinding(utils, httpClient, platformPath, name, binding)
    63  		if err != nil {
    64  			return err
    65  		}
    66  	}
    67  
    68  	return nil
    69  }
    70  
    71  func processBinding(utils cnbutils.BuildUtils, httpClient piperhttp.Sender, platformPath string, name string, binding binding) error {
    72  	err := validateBinding(name, binding)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	bindingDir := filepath.Join(platformPath, "bindings", name)
    78  	err = utils.MkdirAll(bindingDir, 0755)
    79  	if err != nil {
    80  		return errors.Wrap(err, "failed to create binding directory")
    81  	}
    82  
    83  	err = utils.FileWrite(filepath.Join(bindingDir, "type"), []byte(binding.Type), 0644)
    84  	if err != nil {
    85  		return errors.Wrap(err, "failed to write the 'type' binding file")
    86  	}
    87  
    88  	if binding.File != nil {
    89  		_, err = utils.Copy(*binding.File, filepath.Join(bindingDir, binding.Key))
    90  		if err != nil {
    91  			return errors.Wrap(err, "failed to copy binding file")
    92  		}
    93  	} else {
    94  		var bindingContent []byte
    95  
    96  		if binding.Content == nil {
    97  			response, err := httpClient.SendRequest(http.MethodGet, *binding.FromURL, nil, nil, nil)
    98  			if err != nil {
    99  				return errors.Wrap(err, "failed to load binding from url")
   100  			}
   101  
   102  			bindingContent, err = ioutil.ReadAll(response.Body)
   103  			defer response.Body.Close()
   104  			if err != nil {
   105  				return errors.Wrap(err, "error reading response")
   106  			}
   107  		} else {
   108  			bindingContent = []byte(*binding.Content)
   109  		}
   110  
   111  		err = utils.FileWrite(filepath.Join(bindingDir, binding.Key), bindingContent, 0644)
   112  		if err != nil {
   113  			return errors.Wrap(err, "failed to write binding")
   114  		}
   115  	}
   116  
   117  	return nil
   118  }
   119  
   120  func validateBinding(name string, binding binding) error {
   121  	if !validName(name) {
   122  		return fmt.Errorf("invalid binding name: '%s'", name)
   123  	}
   124  
   125  	return binding.validate()
   126  }
   127  
   128  func toTyped(rawData interface{}) (bindings, error) {
   129  	var typedBindings bindings
   130  
   131  	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   132  		ErrorUnused: true,
   133  		Result:      &typedBindings,
   134  	})
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	err = decoder.Decode(rawData)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	return typedBindings, nil
   144  }
   145  
   146  func validName(name string) bool {
   147  	if name == "" || name == "." || name == ".." {
   148  		return false
   149  	}
   150  
   151  	return !strings.ContainsAny(name, "/")
   152  }