github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/docker/dockerfile.go (about)

     1  package docker
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/artisanhe/tools/executil"
    11  	"github.com/artisanhe/tools/godash"
    12  )
    13  
    14  func EnvVarInDocker(key string) string {
    15  	return fmt.Sprintf("$${%s}", key)
    16  }
    17  
    18  func EnvVar(key string) string {
    19  	return fmt.Sprintf("${%s}", key)
    20  }
    21  
    22  type Dockerfile struct {
    23  	From       string            `docker:"FROM" yaml:"from,omitempty"`
    24  	Image      string            `yaml:"image"`
    25  	Label      map[string]string `docker:"LABEL,multi" yaml:"label,omitempty"`
    26  	Run        string            `docker:"RUN,inline" yaml:"run,omitempty"`
    27  	WorkDir    string            `docker:"WORKDIR" yaml:"workdir,omitempty"`
    28  	Env        map[string]string `docker:"ENV,multi" yaml:"env,omitempty"`
    29  	Add        map[string]string `docker:"ADD,join" yaml:"add,omitempty"`
    30  	Expose     []string          `docker:"EXPOSE" yaml:"expose,omitempty"`
    31  	Volume     []string          `docker:"VOLUME,array" yaml:"volume,omitempty"`
    32  	Cmd        []string          `docker:"CMD,array" yaml:"cmd,omitempty"`
    33  	EntryPoint []string          `docker:"ENTRYPOINT,array" yaml:"entrypoint,omitempty"`
    34  }
    35  
    36  func (d *Dockerfile) String() string {
    37  	envVars := executil.EnvVars{}
    38  	envVars.LoadFromEnviron()
    39  	return envVars.Parse(GetDockerfileTemplate(*d))
    40  }
    41  
    42  func (d Dockerfile) AddContent(from string, to string) *Dockerfile {
    43  	if d.Add == nil {
    44  		d.Add = map[string]string{}
    45  	}
    46  	d.Add[from] = to
    47  	return &d
    48  }
    49  
    50  func (d Dockerfile) AddLabel(label string, content string) *Dockerfile {
    51  	if d.Label == nil {
    52  		d.Label = map[string]string{}
    53  	}
    54  	d.Label[label] = content
    55  	return &d
    56  }
    57  
    58  func (d Dockerfile) AddEnv(key string, value string) *Dockerfile {
    59  	if d.Env == nil {
    60  		d.Env = map[string]string{}
    61  	}
    62  	d.Env[key] = value
    63  	return &d
    64  }
    65  
    66  func (d Dockerfile) WithExpose(exposes ...string) *Dockerfile {
    67  	d.Expose = exposes
    68  	return &d
    69  }
    70  
    71  func (d Dockerfile) WithVolume(volumes ...string) *Dockerfile {
    72  	d.Volume = volumes
    73  	return &d
    74  }
    75  
    76  func (d Dockerfile) WithWorkDir(dir string) *Dockerfile {
    77  	d.WorkDir = dir
    78  	return &d
    79  }
    80  
    81  func (d Dockerfile) WithCmd(cmd ...string) *Dockerfile {
    82  	d.Cmd = cmd
    83  	return &d
    84  }
    85  
    86  func GetDockerfileTemplate(d Dockerfile) string {
    87  	dockerfileConfig := make([]string, 0)
    88  
    89  	appendDockerConfig := func(dockerKey string, value string) {
    90  		dockerfileConfig = append(
    91  			dockerfileConfig,
    92  			strings.Join([]string{dockerKey, value}, " "),
    93  		)
    94  	}
    95  
    96  	tpe := reflect.TypeOf(d)
    97  	rv := reflect.Indirect(reflect.ValueOf(d))
    98  
    99  	for i := 0; i < tpe.NumField(); i++ {
   100  		field := tpe.Field(i)
   101  		dockerTag := field.Tag.Get("docker")
   102  		dockerKeys := strings.Split(dockerTag, ",")
   103  		dockerKey := dockerKeys[0]
   104  		dockerFlags := dockerKeys[1:]
   105  
   106  		if len(dockerKey) > 0 {
   107  			value := rv.FieldByName(field.Name)
   108  
   109  			switch field.Type.Kind() {
   110  			case reflect.String:
   111  				if len(value.String()) > 0 {
   112  					inline := godash.StringIncludes(dockerFlags, "inline")
   113  					if inline {
   114  						appendDockerConfig(dockerKey, value.String())
   115  					} else {
   116  						appendDockerConfig(dockerKey, mayQuote(value.String()))
   117  					}
   118  				}
   119  			case reflect.Slice:
   120  				jsonArray := godash.StringIncludes(dockerFlags, "array")
   121  				slice := make([]string, 0)
   122  				for i := 0; i < value.Len(); i++ {
   123  					slice = append(slice, value.Index(i).String())
   124  				}
   125  				if len(slice) > 0 {
   126  					if jsonArray {
   127  						jsonString, err := json.Marshal(slice)
   128  						if err != nil {
   129  							panic(err)
   130  						}
   131  						appendDockerConfig(
   132  							dockerKey,
   133  							string(jsonString),
   134  						)
   135  					} else {
   136  						appendDockerConfig(
   137  							dockerKey,
   138  							strings.Join(slice, ""),
   139  						)
   140  					}
   141  				}
   142  
   143  			case reflect.Map:
   144  				multi := godash.StringIncludes(dockerFlags, "multi")
   145  				join := godash.StringIncludes(dockerFlags, "join")
   146  
   147  				if join {
   148  					destMap := map[string][]string{}
   149  					for _, key := range value.MapKeys() {
   150  						dest := value.MapIndex(key).String()
   151  						if destMap[dest] == nil {
   152  							destMap[dest] = []string{}
   153  						}
   154  						destMap[dest] = append(destMap[dest], key.String())
   155  					}
   156  					for dest, src := range destMap {
   157  						appendDockerConfig(
   158  							dockerKey,
   159  							strings.Join(append(src, dest), " "),
   160  						)
   161  					}
   162  				} else if multi {
   163  					keyValues := []string{}
   164  					for _, key := range value.MapKeys() {
   165  						keyValues = append(
   166  							keyValues,
   167  							key.String()+"="+mayQuote(value.MapIndex(key).String()),
   168  						)
   169  					}
   170  					if len(keyValues) > 0 {
   171  						appendDockerConfig(
   172  							dockerKey,
   173  							strings.Join(keyValues, " "),
   174  						)
   175  					}
   176  				} else {
   177  					for _, key := range value.MapKeys() {
   178  						appendDockerConfig(
   179  							dockerKey,
   180  							strings.Join([]string{key.String(), mayQuote(value.MapIndex(key).String())}, " "),
   181  						)
   182  					}
   183  				}
   184  			}
   185  		}
   186  	}
   187  
   188  	return strings.Join(dockerfileConfig, "\n")
   189  }
   190  
   191  func mayQuote(s string) string {
   192  	if s == "" || strings.Index(s, " ") > -1 {
   193  		return strconv.Quote(s)
   194  	}
   195  	return s
   196  }