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  }