github.com/flatt-online-training/libcompose@v0.4.0/yaml/build.go (about)

     1  package yaml
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // Build represents a build element in compose file.
    11  // It can take multiple form in the compose file, hence this special type
    12  type Build struct {
    13  	Context    string
    14  	Dockerfile string
    15  	Args       map[string]string
    16  }
    17  
    18  // MarshalYAML implements the Marshaller interface.
    19  func (b Build) MarshalYAML() (interface{}, error) {
    20  	m := map[string]interface{}{}
    21  	if b.Context != "" {
    22  		m["context"] = b.Context
    23  	}
    24  	if b.Dockerfile != "" {
    25  		m["dockerfile"] = b.Dockerfile
    26  	}
    27  	if len(b.Args) > 0 {
    28  		m["args"] = b.Args
    29  	}
    30  	return m, nil
    31  }
    32  
    33  // UnmarshalYAML implements the Unmarshaller interface.
    34  func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error {
    35  	var stringType string
    36  	if err := unmarshal(&stringType); err == nil {
    37  		b.Context = stringType
    38  		return nil
    39  	}
    40  
    41  	var mapType map[interface{}]interface{}
    42  	if err := unmarshal(&mapType); err == nil {
    43  		for mapKey, mapValue := range mapType {
    44  			switch mapKey {
    45  			case "context":
    46  				b.Context = mapValue.(string)
    47  			case "dockerfile":
    48  				b.Dockerfile = mapValue.(string)
    49  			case "args":
    50  				args, err := handleBuildArgs(mapValue)
    51  				if err != nil {
    52  					return err
    53  				}
    54  				b.Args = args
    55  			default:
    56  				// Ignore unknown keys
    57  				continue
    58  			}
    59  		}
    60  		return nil
    61  	}
    62  
    63  	return errors.New("Failed to unmarshal Build")
    64  }
    65  
    66  func handleBuildArgs(value interface{}) (map[string]string, error) {
    67  	var args map[string]string
    68  	switch v := value.(type) {
    69  	case map[interface{}]interface{}:
    70  		return handleBuildArgMap(v)
    71  	case []interface{}:
    72  		return handleBuildArgSlice(v)
    73  	default:
    74  		return args, fmt.Errorf("Failed to unmarshal Build args: %#v", value)
    75  	}
    76  }
    77  
    78  func handleBuildArgSlice(s []interface{}) (map[string]string, error) {
    79  	var args = map[string]string{}
    80  	for _, arg := range s {
    81  		// check if a value is provided
    82  		switch v := strings.SplitN(arg.(string), "=", 2); len(v) {
    83  		case 1:
    84  			// if we have not specified a a value for this build arg, we assign it an ascii null value and query the environment
    85  			// later when we build the service
    86  			args[v[0]] = "\x00"
    87  		case 2:
    88  			// if we do have a value provided, we use it
    89  			args[v[0]] = v[1]
    90  		}
    91  	}
    92  	return args, nil
    93  }
    94  
    95  func handleBuildArgMap(m map[interface{}]interface{}) (map[string]string, error) {
    96  	args := map[string]string{}
    97  	for mapKey, mapValue := range m {
    98  		var argValue string
    99  		name, ok := mapKey.(string)
   100  		if !ok {
   101  			return args, fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", name, name)
   102  		}
   103  		switch a := mapValue.(type) {
   104  		case string:
   105  			argValue = a
   106  		case int:
   107  			argValue = strconv.Itoa(a)
   108  		case int64:
   109  			argValue = strconv.Itoa(int(a))
   110  		default:
   111  			return args, fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", mapValue, mapValue)
   112  		}
   113  		args[name] = argValue
   114  	}
   115  	return args, nil
   116  }