github.com/docker/buildx@v0.14.1-0.20240514123050-afcb609966dc/store/nodegroup.go (about) 1 package store 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/containerd/containerd/platforms" 8 "github.com/docker/buildx/util/confutil" 9 "github.com/docker/buildx/util/platformutil" 10 specs "github.com/opencontainers/image-spec/specs-go/v1" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 ) 14 15 type NodeGroup struct { 16 Name string 17 Driver string 18 Nodes []Node 19 Dynamic bool 20 21 // skip the following fields from being saved in the store 22 DockerContext bool `json:"-"` 23 LastActivity time.Time `json:"-"` 24 } 25 26 type Node struct { 27 Name string 28 Endpoint string 29 Platforms []specs.Platform 30 DriverOpts map[string]string 31 BuildkitdFlags []string `json:"Flags"` // keep the field name for backward compatibility 32 33 Files map[string][]byte 34 } 35 36 func (ng *NodeGroup) Leave(name string) error { 37 if ng.Dynamic { 38 return errors.New("dynamic node group does not support Leave") 39 } 40 i := ng.findNode(name) 41 if i == -1 { 42 return errors.Errorf("node %q not found for %s", name, ng.Name) 43 } 44 if len(ng.Nodes) == 1 { 45 return errors.Errorf("can not leave last node, do you want to rm instance instead?") 46 } 47 ng.Nodes = append(ng.Nodes[:i], ng.Nodes[i+1:]...) 48 return nil 49 } 50 51 func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, buildkitdFlags []string, buildkitdConfigFile string, do map[string]string) error { 52 if ng.Dynamic { 53 return errors.New("dynamic node group does not support Update") 54 } 55 i := ng.findNode(name) 56 if i == -1 && !actionAppend { 57 if len(ng.Nodes) > 0 { 58 return errors.Errorf("node %s not found, did you mean to append?", name) 59 } 60 ng.Nodes = nil 61 } 62 63 pp, err := platformutil.Parse(platforms) 64 if err != nil { 65 return err 66 } 67 68 var files map[string][]byte 69 if buildkitdConfigFile != "" { 70 files, err = confutil.LoadConfigFiles(buildkitdConfigFile) 71 if err != nil { 72 return err 73 } 74 } 75 76 if i != -1 { 77 n := ng.Nodes[i] 78 needsRestart := false 79 if endpointsSet { 80 n.Endpoint = endpoint 81 needsRestart = true 82 } 83 if len(platforms) > 0 { 84 n.Platforms = pp 85 } 86 if buildkitdFlags != nil { 87 n.BuildkitdFlags = buildkitdFlags 88 needsRestart = true 89 } 90 if do != nil { 91 n.DriverOpts = do 92 needsRestart = true 93 } 94 if buildkitdConfigFile != "" { 95 for k, v := range files { 96 n.Files[k] = v 97 } 98 needsRestart = true 99 } 100 if needsRestart { 101 logrus.Warn("new settings may not be used until builder is restarted") 102 } 103 104 ng.Nodes[i] = n 105 return ng.validateDuplicates(endpoint, i) 106 } 107 108 if name == "" { 109 name = ng.nextNodeName() 110 } 111 112 name, err = ValidateName(name) 113 if err != nil { 114 return err 115 } 116 117 n := Node{ 118 Name: name, 119 Endpoint: endpoint, 120 Platforms: pp, 121 DriverOpts: do, 122 BuildkitdFlags: buildkitdFlags, 123 Files: files, 124 } 125 126 ng.Nodes = append(ng.Nodes, n) 127 return ng.validateDuplicates(endpoint, len(ng.Nodes)-1) 128 } 129 130 func (ng *NodeGroup) Copy() *NodeGroup { 131 nodes := make([]Node, len(ng.Nodes)) 132 for i, node := range ng.Nodes { 133 nodes[i] = *node.Copy() 134 } 135 return &NodeGroup{ 136 Name: ng.Name, 137 Driver: ng.Driver, 138 Nodes: nodes, 139 Dynamic: ng.Dynamic, 140 } 141 } 142 143 func (n *Node) Copy() *Node { 144 platforms := []specs.Platform{} 145 copy(platforms, n.Platforms) 146 buildkitdFlags := []string{} 147 copy(buildkitdFlags, n.BuildkitdFlags) 148 driverOpts := map[string]string{} 149 for k, v := range n.DriverOpts { 150 driverOpts[k] = v 151 } 152 files := map[string][]byte{} 153 for k, v := range n.Files { 154 vv := []byte{} 155 copy(vv, v) 156 files[k] = vv 157 } 158 return &Node{ 159 Name: n.Name, 160 Endpoint: n.Endpoint, 161 Platforms: platforms, 162 BuildkitdFlags: buildkitdFlags, 163 DriverOpts: driverOpts, 164 Files: files, 165 } 166 } 167 168 func (ng *NodeGroup) validateDuplicates(ep string, idx int) error { 169 i := 0 170 for _, n := range ng.Nodes { 171 if n.Endpoint == ep { 172 i++ 173 } 174 } 175 if i > 1 { 176 return errors.Errorf("invalid duplicate endpoint %s", ep) 177 } 178 179 m := map[string]struct{}{} 180 for _, p := range ng.Nodes[idx].Platforms { 181 m[platforms.Format(p)] = struct{}{} 182 } 183 184 for i := range ng.Nodes { 185 if i == idx { 186 continue 187 } 188 ng.Nodes[i].Platforms = filterPlatforms(ng.Nodes[i].Platforms, m) 189 } 190 191 return nil 192 } 193 194 func (ng *NodeGroup) findNode(name string) int { 195 for i, n := range ng.Nodes { 196 if n.Name == name { 197 return i 198 } 199 } 200 return -1 201 } 202 203 func (ng *NodeGroup) nextNodeName() string { 204 i := 0 205 for { 206 name := fmt.Sprintf("%s%d", ng.Name, i) 207 if ii := ng.findNode(name); ii != -1 { 208 i++ 209 continue 210 } 211 return name 212 } 213 } 214 215 func filterPlatforms(in []specs.Platform, m map[string]struct{}) []specs.Platform { 216 out := make([]specs.Platform, 0, len(in)) 217 for _, p := range in { 218 if _, ok := m[platforms.Format(p)]; !ok { 219 out = append(out, p) 220 } 221 } 222 return out 223 }