github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/oci/config/generate/spec.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016, 2017, 2018 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 inital 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 for _, v := range g.image.Config.Env { 166 copy = append(copy, v) 167 } 168 return copy 169 } 170 171 // ClearConfigEntrypoint clears the list of arguments to use as the command to execute when the container starts. 172 func (g *Generator) ClearConfigEntrypoint() { 173 g.image.Config.Entrypoint = []string{} 174 } 175 176 // SetConfigEntrypoint sets the list of arguments to use as the command to execute when the container starts. 177 func (g *Generator) SetConfigEntrypoint(entrypoint []string) { 178 copy := []string{} 179 for _, v := range entrypoint { 180 copy = append(copy, v) 181 } 182 g.image.Config.Entrypoint = copy 183 } 184 185 // ConfigEntrypoint returns the list of arguments to use as the command to execute when the container starts. 186 func (g *Generator) ConfigEntrypoint() []string { 187 // We have to make a copy to preserve the privacy of g.image.Config. 188 copy := []string{} 189 for _, v := range g.image.Config.Entrypoint { 190 copy = append(copy, v) 191 } 192 return copy 193 } 194 195 // ClearConfigCmd clears the list of default arguments to the entrypoint of the container. 196 func (g *Generator) ClearConfigCmd() { 197 g.image.Config.Cmd = []string{} 198 } 199 200 // SetConfigCmd sets the list of default arguments to the entrypoint of the container. 201 func (g *Generator) SetConfigCmd(cmd []string) { 202 copy := []string{} 203 for _, v := range cmd { 204 copy = append(copy, v) 205 } 206 g.image.Config.Cmd = copy 207 } 208 209 // ConfigCmd returns the list of default arguments to the entrypoint of the container. 210 func (g *Generator) ConfigCmd() []string { 211 // We have to make a copy to preserve the privacy of g.image.Config. 212 copy := []string{} 213 for _, v := range g.image.Config.Cmd { 214 copy = append(copy, v) 215 } 216 return copy 217 } 218 219 // ClearConfigVolumes clears the set of directories which should be created as data volumes in a container running this image. 220 func (g *Generator) ClearConfigVolumes() { 221 g.image.Config.Volumes = map[string]struct{}{} 222 } 223 224 // AddConfigVolume adds a volume to the set of directories which should be created as data volumes in a container running this image. 225 func (g *Generator) AddConfigVolume(volume string) { 226 g.image.Config.Volumes[volume] = struct{}{} 227 } 228 229 // RemoveConfigVolume removes a volume from the set of directories which should be created as data volumes in a container running this image. 230 func (g *Generator) RemoveConfigVolume(volume string) { 231 delete(g.image.Config.Volumes, volume) 232 } 233 234 // ConfigVolumes returns the set of directories which should be created as data volumes in a container running this image. 235 func (g *Generator) ConfigVolumes() map[string]struct{} { 236 // We have to make a copy to preserve the privacy of g.image.Config. 237 copy := map[string]struct{}{} 238 for k, v := range g.image.Config.Volumes { 239 copy[k] = v 240 } 241 return copy 242 } 243 244 // ClearConfigLabels clears the set of arbitrary metadata for the container. 245 func (g *Generator) ClearConfigLabels() { 246 g.image.Config.Labels = map[string]string{} 247 } 248 249 // AddConfigLabel adds a label to the set of arbitrary metadata for the container. 250 func (g *Generator) AddConfigLabel(label, value string) { 251 g.image.Config.Labels[label] = value 252 } 253 254 // RemoveConfigLabel removes a label from the set of arbitrary metadata for the container. 255 func (g *Generator) RemoveConfigLabel(label string) { 256 delete(g.image.Config.Labels, label) 257 } 258 259 // ConfigLabels returns the set of arbitrary metadata for the container. 260 func (g *Generator) ConfigLabels() map[string]string { 261 // We have to make a copy to preserve the privacy of g.image.Config. 262 copy := map[string]string{} 263 for k, v := range g.image.Config.Labels { 264 copy[k] = v 265 } 266 return copy 267 } 268 269 // SetConfigWorkingDir sets the current working directory of the entrypoint process in the container. 270 func (g *Generator) SetConfigWorkingDir(workingDir string) { 271 g.image.Config.WorkingDir = workingDir 272 } 273 274 // ConfigWorkingDir returns the current working directory of the entrypoint process in the container. 275 func (g *Generator) ConfigWorkingDir() string { 276 return g.image.Config.WorkingDir 277 } 278 279 // SetConfigStopSignal sets the system call signal that will be sent to the container to exit. 280 func (g *Generator) SetConfigStopSignal(stopSignal string) { 281 g.image.Config.StopSignal = stopSignal 282 } 283 284 // ConfigStopSignal returns the system call signal that will be sent to the container to exit. 285 func (g *Generator) ConfigStopSignal() string { 286 return g.image.Config.StopSignal 287 } 288 289 // SetRootfsType sets the type of the rootfs. 290 func (g *Generator) SetRootfsType(rootfsType string) { 291 g.image.RootFS.Type = rootfsType 292 } 293 294 // RootfsType returns the type of the rootfs. 295 func (g *Generator) RootfsType() string { 296 return g.image.RootFS.Type 297 } 298 299 // ClearRootfsDiffIDs clears the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 300 func (g *Generator) ClearRootfsDiffIDs() { 301 g.image.RootFS.DiffIDs = []digest.Digest{} 302 } 303 304 // AddRootfsDiffID appends to the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 305 func (g *Generator) AddRootfsDiffID(diffid digest.Digest) { 306 g.image.RootFS.DiffIDs = append(g.image.RootFS.DiffIDs, diffid) 307 } 308 309 // RootfsDiffIDs returns the the array of layer content hashes (DiffIDs), in order from bottom-most to top-most. 310 func (g *Generator) RootfsDiffIDs() []digest.Digest { 311 copy := []digest.Digest{} 312 for _, v := range g.image.RootFS.DiffIDs { 313 copy = append(copy, v) 314 } 315 return copy 316 } 317 318 // ClearHistory clears the history of each layer. 319 func (g *Generator) ClearHistory() { 320 g.image.History = []ispec.History{} 321 } 322 323 // AddHistory appends to the history of the layers. 324 func (g *Generator) AddHistory(history ispec.History) { 325 g.image.History = append(g.image.History, history) 326 } 327 328 // History returns the history of each layer. 329 func (g *Generator) History() []ispec.History { 330 copy := []ispec.History{} 331 for _, v := range g.image.History { 332 copy = append(copy, v) 333 } 334 return copy 335 } 336 337 // ISO8601 represents the format of an ISO-8601 time string, which is identical 338 // to Go's RFC3339 specification. 339 const ISO8601 = time.RFC3339Nano 340 341 // SetCreated sets the combined date and time at which the image was created. 342 func (g *Generator) SetCreated(created time.Time) { 343 g.image.Created = &created 344 } 345 346 // Created gets the combined date and time at which the image was created. 347 func (g *Generator) Created() time.Time { 348 if g.image.Created == nil { 349 // TODO: Maybe we should be returning pointers? 350 return time.Time{} 351 } 352 return *g.image.Created 353 } 354 355 // SetAuthor sets the name and/or email address of the person or entity which created and is responsible for maintaining the image. 356 func (g *Generator) SetAuthor(author string) { 357 g.image.Author = author 358 } 359 360 // Author returns the name and/or email address of the person or entity which created and is responsible for maintaining the image. 361 func (g *Generator) Author() string { 362 return g.image.Author 363 } 364 365 // SetArchitecture is the CPU architecture which the binaries in this image are built to run on. 366 func (g *Generator) SetArchitecture(arch string) { 367 g.image.Architecture = arch 368 } 369 370 // Architecture returns the CPU architecture which the binaries in this image are built to run on. 371 func (g *Generator) Architecture() string { 372 return g.image.Architecture 373 } 374 375 // SetOS sets the name of the operating system which the image is built to run on. 376 func (g *Generator) SetOS(os string) { 377 g.image.OS = os 378 } 379 380 // OS returns the name of the operating system which the image is built to run on. 381 func (g *Generator) OS() string { 382 return g.image.OS 383 }