package llb import ( "fmt" "net" "path" "github.com/containerd/containerd/platforms" "github.com/google/shlex" "github.com/moby/buildkit/solver/pb" specs "github.com/opencontainers/image-spec/specs-go/v1" ) type contextKeyT string var ( keyArgs = contextKeyT("llb.exec.args") keyDir = contextKeyT("llb.exec.dir") keyEnv = contextKeyT("llb.exec.env") keyUser = contextKeyT("llb.exec.user") keyExtraHost = contextKeyT("llb.exec.extrahost") keyPlatform = contextKeyT("llb.platform") keyNetwork = contextKeyT("llb.network") ) func addEnvf(key, value string, v ...interface{}) StateOption { return func(s State) State { return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, fmt.Sprintf(value, v...))) } } func dir(str string) StateOption { return dirf(str) } func dirf(str string, v ...interface{}) StateOption { return func(s State) State { value := fmt.Sprintf(str, v...) if !path.IsAbs(value) { prev := getDir(s) if prev == "" { prev = "/" } value = path.Join(prev, value) } return s.WithValue(keyDir, value) } } func user(str string) StateOption { return func(s State) State { return s.WithValue(keyUser, str) } } func reset(s_ State) StateOption { return func(s State) State { s = NewState(s.Output()) s.ctx = s_.ctx return s } } func getEnv(s State) EnvList { v := s.Value(keyEnv) if v != nil { return v.(EnvList) } return EnvList{} } func getDir(s State) string { v := s.Value(keyDir) if v != nil { return v.(string) } return "" } func getArgs(s State) []string { v := s.Value(keyArgs) if v != nil { return v.([]string) } return nil } func getUser(s State) string { v := s.Value(keyUser) if v != nil { return v.(string) } return "" } func args(args ...string) StateOption { return func(s State) State { return s.WithValue(keyArgs, args) } } func shlexf(str string, v ...interface{}) StateOption { return func(s State) State { arg, err := shlex.Split(fmt.Sprintf(str, v...)) if err != nil { // TODO: handle error } return args(arg...)(s) } } func platform(p specs.Platform) StateOption { return func(s State) State { return s.WithValue(keyPlatform, platforms.Normalize(p)) } } func getPlatform(s State) *specs.Platform { v := s.Value(keyPlatform) if v != nil { p := v.(specs.Platform) return &p } return nil } func extraHost(host string, ip net.IP) StateOption { return func(s State) State { return s.WithValue(keyExtraHost, append(getExtraHosts(s), HostIP{Host: host, IP: ip})) } } func getExtraHosts(s State) []HostIP { v := s.Value(keyExtraHost) if v != nil { return v.([]HostIP) } return nil } type HostIP struct { Host string IP net.IP } func network(v pb.NetMode) StateOption { return func(s State) State { return s.WithValue(keyNetwork, v) } } func getNetwork(s State) pb.NetMode { v := s.Value(keyNetwork) if v != nil { n := v.(pb.NetMode) return n } return NetModeSandbox } type EnvList []KeyValue type KeyValue struct { key string value string } func (e EnvList) AddOrReplace(k, v string) EnvList { e = e.Delete(k) e = append(e, KeyValue{key: k, value: v}) return e } func (e EnvList) SetDefault(k, v string) EnvList { if _, ok := e.Get(k); !ok { e = append(e, KeyValue{key: k, value: v}) } return e } func (e EnvList) Delete(k string) EnvList { e = append([]KeyValue(nil), e...) if i, ok := e.Index(k); ok { return append(e[:i], e[i+1:]...) } return e } func (e EnvList) Get(k string) (string, bool) { if index, ok := e.Index(k); ok { return e[index].value, true } return "", false } func (e EnvList) Index(k string) (int, bool) { for i, kv := range e { if kv.key == k { return i, true } } return -1, false } func (e EnvList) ToArray() []string { out := make([]string, 0, len(e)) for _, kv := range e { out = append(out, kv.key+"="+kv.value) } return out }