github.com/bdwilliams/libcompose@v0.3.1-0.20160826154243-d81a9bdacff0/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 }