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  }