github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/oci/config/generate/spec.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package generate 19 20 import ( 21 "fmt" 22 "sort" 23 "strings" 24 "time" 25 26 "github.com/opencontainers/go-digest" 27 ispec "github.com/opencontainers/image-spec/specs-go/v1" 28 ) 29 30 // FIXME: Because we are not a part of upstream, we have to add some tests that 31 // ensure that this set of getters and setters is complete. This should 32 // be possible through some reflection. 33 34 // Generator allows you to generate a mutable OCI image-spec configuration 35 // which can be written to a file (and its digest computed). It is the 36 // recommended way of handling modification and generation of image-spec 37 // configuration blobs. 38 type Generator struct { 39 image ispec.Image 40 } 41 42 // init makes sure everything has a "proper" zero value. 43 func (g *Generator) init() { 44 if g.image.Config.ExposedPorts == nil { 45 g.ClearConfigExposedPorts() 46 } 47 if g.image.Config.Env == nil { 48 g.ClearConfigEnv() 49 } 50 if g.image.Config.Entrypoint == nil { 51 g.ClearConfigEntrypoint() 52 } 53 if g.image.Config.Cmd == nil { 54 g.ClearConfigCmd() 55 } 56 if g.image.Config.Volumes == nil { 57 g.ClearConfigVolumes() 58 } 59 if g.image.Config.Labels == nil { 60 g.ClearConfigLabels() 61 } 62 if g.image.RootFS.DiffIDs == nil { 63 g.ClearRootfsDiffIDs() 64 } 65 if g.image.History == nil { 66 g.ClearHistory() 67 } 68 } 69 70 // New creates a new Generator with the initial template set to a default. It is 71 // not recommended to leave any of the options as their default values (they 72 // may change in the future without warning and may be invalid images). 73 func New() *Generator { 74 // FIXME: Come up with some sane default. 75 g := &Generator{ 76 image: ispec.Image{}, 77 } 78 g.init() 79 return g 80 } 81 82 // NewFromImage generates a new generator with the initial template being the 83 // given ispec.Image. 84 func NewFromImage(image ispec.Image) (*Generator, error) { 85 g := &Generator{ 86 image: image, 87 } 88 89 g.init() 90 return g, nil 91 } 92 93 // Image returns a copy of the current state of the generated image. 94 func (g *Generator) Image() ispec.Image { 95 return g.image 96 } 97 98 // SetConfigUser sets the username or UID which the process in the container should run as. 99 func (g *Generator) SetConfigUser(user string) { 100 g.image.Config.User = user 101 } 102 103 // ConfigUser returns the username or UID which the process in the container should run as. 104 func (g *Generator) ConfigUser() string { 105 return g.image.Config.User 106 } 107 108 // ClearConfigExposedPorts clears the set of ports to expose from a container running this image. 109 func (g *Generator) ClearConfigExposedPorts() { 110 g.image.Config.ExposedPorts = map[string]struct{}{} 111 } 112 113 // AddConfigExposedPort adds a port the set of ports to expose from a container running this image. 114 func (g *Generator) AddConfigExposedPort(port string) { 115 g.image.Config.ExposedPorts[port] = struct{}{} 116 } 117 118 // RemoveConfigExposedPort removes a port the set of ports to expose from a container running this image. 119 func (g *Generator) RemoveConfigExposedPort(port string) { 120 delete(g.image.Config.ExposedPorts, port) 121 } 122 123 // ConfigExposedPorts returns the set of ports to expose from a container running this image. 124 func (g *Generator) ConfigExposedPorts() map[string]struct{} { 125 // We have to make a copy to preserve the privacy of g.image.Config. 126 copy := map[string]struct{}{} 127 for k, v := range g.image.Config.ExposedPorts { 128 copy[k] = v 129 } 130 return copy 131 } 132 133 // ConfigExposedPortsArray returns a sorted array of ports to expose from a container running this image. 134 func (g *Generator) ConfigExposedPortsArray() []string { 135 var ports []string 136 for port := range g.image.Config.ExposedPorts { 137 ports = append(ports, port) 138 } 139 sort.Strings(ports) 140 return ports 141 } 142 143 // ClearConfigEnv clears the list of environment variables to be used in a container. 144 func (g *Generator) ClearConfigEnv() { 145 g.image.Config.Env = []string{} 146 } 147 148 // AddConfigEnv appends to the list of environment variables to be used in a container. 149 func (g *Generator) AddConfigEnv(name, value string) { 150 // If the key already exists in the environment set, we replace it. 151 // This ensures we don't run into POSIX undefined territory. 152 env := fmt.Sprintf("%s=%s", name, value) 153 for idx := range g.image.Config.Env { 154 if strings.HasPrefix(g.image.Config.Env[idx], name+"=") { 155 g.image.Config.Env[idx] = env 156 return 157 } 158 } 159 g.image.Config.Env = append(g.image.Config.Env, env) 160 } 161 162 // ConfigEnv returns the list of environment variables to be used in a container. 163 func (g *Generator) ConfigEnv() []string { 164 copy := []string{} 165 copy = append(copy, g.image.Config.Env...) 166 return copy 167 } 168 169 // ClearConfigEntrypoint clears the list of arguments to use as the command to execute when the container starts. 170 func (g *Generator) ClearConfigEntrypoint() { 171 g.image.Config.Entrypoint = []string{} 172 } 173 174 // SetConfigEntrypoint sets the list of arguments to use as the command to execute when the container starts. 175 func (g *Generator) SetConfigEntrypoint(entrypoint []string) { 176 copy := []string{} 177 copy = append(copy, entrypoint...) 178 g.image.Config.Entrypoint = copy 179 } 180 181 // ConfigEntrypoint returns the list of arguments to use as the command to execute when the container starts. 182 func (g *Generator) ConfigEntrypoint() []string { 183 // We have to make a copy to preserve the privacy of g.image.Config. 184 copy := []string{} 185 copy = append(copy, g.image.Config.Entrypoint...) 186 return copy 187 } 188 189 // ClearConfigCmd clears the list of default arguments to the entrypoint of the container. 190 func (g *Generator) ClearConfigCmd() { 191 g.image.Config.Cmd = []string{} 192 } 193 194 // SetConfigCmd sets the list of default arguments to the entrypoint of the container. 195 func (g *Generator) SetConfigCmd(cmd []string) { 196 copy := []string{} 197 copy = append(copy, cmd...) 198 g.image.Config.Cmd = copy 199 } 200 201 // ConfigCmd returns the list of default arguments to the entrypoint of the container. 202 func (g *Generator) ConfigCmd() []string { 203 // We have to make a copy to preserve the privacy of g.image.Config. 204 copy := []string{} 205 copy = append(copy, g.image.Config.Cmd...) 206 return copy 207 } 208 209 // ClearConfigVolumes clears the set of directories which should be created as data volumes in a container running this image. 210 func (g *Generator) ClearConfigVolumes() { 211 g.image.Config.Volumes = map[string]struct{}{} 212 } 213 214 // AddConfigVolume adds a volume to the set of directories which should be created as data volumes in a container running this image. 215 func (g *Generator) AddConfigVolume(volume string) { 216 g.image.Config.Volumes[volume] = struct{}{} 217 } 218 219 // RemoveConfigVolume removes a volume from the set of directories which should be created as data volumes in a container running this image. 220 func (g *Generator) RemoveConfigVolume(volume string) { 221 delete(g.image.Config.Volumes, volume) 222 } 223 224 // ConfigVolumes returns the set of directories which should be created as data volumes in a container running this image. 225 func (g *Generator) ConfigVolumes() map[string]struct{} { 226 // We have to make a copy to preserve the privacy of g.image.Config. 227 copy := map[string]struct{}{} 228 for k, v := range g.image.Config.Volumes { 229 copy[k] = v 230 } 231 return copy 232 } 233 234 // ClearConfigLabels clears the set of arbitrary metadata for the container. 235 func (g *Generator) ClearConfigLabels() { 236 g.image.Config.Labels = map[string]string{} 237 } 238 239 // AddConfigLabel adds a label to the set of arbitrary metadata for the container. 240 func (g *Generator) AddConfigLabel(label, value string) { 241 g.image.Config.Labels[label] = value 242 } 243 244 // RemoveConfigLabel removes a label from the set of arbitrary metadata for the container. 245 func (g *Generator) RemoveConfigLabel(label string) { 246 delete(g.image.Config.Labels, label) 247 } 248 249 // ConfigLabels returns the set of arbitrary metadata for the container. 250 func (g *Generator) ConfigLabels() map[string]string { 251 // We have to make a copy to preserve the privacy of g.image.Config. 252 copy := map[string]string{} 253 for k, v := range g.image.Config.Labels { 254 copy[k] = v 255 } 256 return copy 257 } 258 259 // SetConfigWorkingDir sets the current working directory of the entrypoint process in the container. 260 func (g *Generator) SetConfigWorkingDir(workingDir string) { 261 g.image.Config.WorkingDir = workingDir 262 } 263 264 // ConfigWorkingDir returns the current working directory of the entrypoint process in the container. 265 func (g *Generator) ConfigWorkingDir() string { 266 return g.image.Config.WorkingDir 267 } 268 269 // SetConfigStopSignal sets the system call signal that will be sent to the container to exit. 270 func (g *Generator) SetConfigStopSignal(stopSignal string) { 271 g.image.Config.StopSignal = stopSignal 272 } 273 274 // ConfigStopSignal returns the system call signal that will be sent to the container to exit. 275 func (g *Generator) ConfigStopSignal() string { 276 return g.image.Config.StopSignal 277 } 278 279 // SetRootfsType sets the type of the rootfs. 280 func (g *Generator) SetRootfsType(rootfsType string) { 281 g.image.RootFS.Type = rootfsType 282 } 283 284 // RootfsType returns the type of the rootfs. 285 func (g *Generator) RootfsType() string { 286 return g.image.RootFS.Type 287 } 288 289 // ClearRootfsDiffIDs clears the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 290 func (g *Generator) ClearRootfsDiffIDs() { 291 g.image.RootFS.DiffIDs = []digest.Digest{} 292 } 293 294 // AddRootfsDiffID appends to the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 295 func (g *Generator) AddRootfsDiffID(diffid digest.Digest) { 296 g.image.RootFS.DiffIDs = append(g.image.RootFS.DiffIDs, diffid) 297 } 298 299 // RootfsDiffIDs returns the the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 300 func (g *Generator) RootfsDiffIDs() []digest.Digest { 301 copy := []digest.Digest{} 302 copy = append(copy, g.image.RootFS.DiffIDs...) 303 return copy 304 } 305 306 // ClearHistory clears the history of each layer. 307 func (g *Generator) ClearHistory() { 308 g.image.History = []ispec.History{} 309 } 310 311 // AddHistory appends to the history of the layers. 312 func (g *Generator) AddHistory(history ispec.History) { 313 g.image.History = append(g.image.History, history) 314 } 315 316 // History returns the history of each layer. 317 func (g *Generator) History() []ispec.History { 318 copy := []ispec.History{} 319 copy = append(copy, g.image.History...) 320 return copy 321 } 322 323 // ISO8601 represents the format of an ISO-8601 time string, which is identical 324 // to Go's RFC3339 specification. 325 const ISO8601 = time.RFC3339Nano 326 327 // SetCreated sets the combined date and time at which the image was created. 328 func (g *Generator) SetCreated(created time.Time) { 329 g.image.Created = &created 330 } 331 332 // Created gets the combined date and time at which the image was created. 333 func (g *Generator) Created() time.Time { 334 if g.image.Created == nil { 335 // TODO: Maybe we should be returning pointers? 336 return time.Time{} 337 } 338 return *g.image.Created 339 } 340 341 // SetAuthor sets the name and/or email address of the person or entity which created and is responsible for maintaining the image. 342 func (g *Generator) SetAuthor(author string) { 343 g.image.Author = author 344 } 345 346 // Author returns the name and/or email address of the person or entity which created and is responsible for maintaining the image. 347 func (g *Generator) Author() string { 348 return g.image.Author 349 } 350 351 // SetArchitecture is the CPU architecture which the binaries in this image are built to run on. 352 func (g *Generator) SetArchitecture(arch string) { 353 g.image.Architecture = arch 354 } 355 356 // Architecture returns the CPU architecture which the binaries in this image are built to run on. 357 func (g *Generator) Architecture() string { 358 return g.image.Architecture 359 } 360 361 // SetOS sets the name of the operating system which the image is built to run on. 362 func (g *Generator) SetOS(os string) { 363 g.image.OS = os 364 } 365 366 // OS returns the name of the operating system which the image is built to run on. 367 func (g *Generator) OS() string { 368 return g.image.OS 369 }