// Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "encoding/json" "io" "time" ) // ConfigFile is the configuration file that holds the metadata describing // how to launch a container. See: // https://github.com/opencontainers/image-spec/blob/master/config.md // // docker_version and os.version are not part of the spec but included // for backwards compatibility. type ConfigFile struct { Architecture string `json:"architecture"` Author string `json:"author,omitempty"` Container string `json:"container,omitempty"` Created Time `json:"created,omitempty"` DockerVersion string `json:"docker_version,omitempty"` History []History `json:"history,omitempty"` OS string `json:"os"` RootFS RootFS `json:"rootfs"` Config Config `json:"config"` OSVersion string `json:"os.version,omitempty"` Variant string `json:"variant,omitempty"` OSFeatures []string `json:"os.features,omitempty"` } // Platform attempts to generates a Platform from the ConfigFile fields. func (cf *ConfigFile) Platform() *Platform { if cf.OS == "" && cf.Architecture == "" && cf.OSVersion == "" && cf.Variant == "" && len(cf.OSFeatures) == 0 { return nil } return &Platform{ OS: cf.OS, Architecture: cf.Architecture, OSVersion: cf.OSVersion, Variant: cf.Variant, OSFeatures: cf.OSFeatures, } } // History is one entry of a list recording how this container image was built. type History struct { Author string `json:"author,omitempty"` Created Time `json:"created,omitempty"` CreatedBy string `json:"created_by,omitempty"` Comment string `json:"comment,omitempty"` EmptyLayer bool `json:"empty_layer,omitempty"` } // Time is a wrapper around time.Time to help with deep copying type Time struct { time.Time } // DeepCopyInto creates a deep-copy of the Time value. The underlying time.Time // type is effectively immutable in the time API, so it is safe to // copy-by-assign, despite the presence of (unexported) Pointer fields. func (t *Time) DeepCopyInto(out *Time) { *out = *t } // RootFS holds the ordered list of file system deltas that comprise the // container image's root filesystem. type RootFS struct { Type string `json:"type"` DiffIDs []Hash `json:"diff_ids"` } // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { // Test is the test to perform to check that the container is healthy. // An empty slice means to inherit the default. // The options are: // {} : inherit healthcheck // {"NONE"} : disable healthcheck // {"CMD", args...} : exec arguments directly // {"CMD-SHELL", command} : run command with system's default shell Test []string `json:",omitempty"` // Zero means to inherit. Durations are expressed as integer nanoseconds. Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. // Retries is the number of consecutive failures needed to consider a container as unhealthy. // Zero means inherit. Retries int `json:",omitempty"` } // Config is a submessage of the config file described as: // // The execution parameters which SHOULD be used as a base when running // a container using the image. // // The names of the fields in this message are chosen to reflect the JSON // payload of the Config as defined here: // https://git.io/vrAET // and // https://github.com/opencontainers/image-spec/blob/master/config.md type Config struct { AttachStderr bool `json:"AttachStderr,omitempty"` AttachStdin bool `json:"AttachStdin,omitempty"` AttachStdout bool `json:"AttachStdout,omitempty"` Cmd []string `json:"Cmd,omitempty"` Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` Domainname string `json:"Domainname,omitempty"` Entrypoint []string `json:"Entrypoint,omitempty"` Env []string `json:"Env,omitempty"` Hostname string `json:"Hostname,omitempty"` Image string `json:"Image,omitempty"` Labels map[string]string `json:"Labels,omitempty"` OnBuild []string `json:"OnBuild,omitempty"` OpenStdin bool `json:"OpenStdin,omitempty"` StdinOnce bool `json:"StdinOnce,omitempty"` Tty bool `json:"Tty,omitempty"` User string `json:"User,omitempty"` Volumes map[string]struct{} `json:"Volumes,omitempty"` WorkingDir string `json:"WorkingDir,omitempty"` ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` ArgsEscaped bool `json:"ArgsEscaped,omitempty"` NetworkDisabled bool `json:"NetworkDisabled,omitempty"` MacAddress string `json:"MacAddress,omitempty"` StopSignal string `json:"StopSignal,omitempty"` Shell []string `json:"Shell,omitempty"` } // ParseConfigFile parses the io.Reader's contents into a ConfigFile. func ParseConfigFile(r io.Reader) (*ConfigFile, error) { cf := ConfigFile{} if err := json.NewDecoder(r).Decode(&cf); err != nil { return nil, err } return &cf, nil }