toolbox/src/pkg/utils/utils.go

950 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © 2019 2025 Red Hat Inc.
*
* 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 utils
import (
"errors"
"fmt"
"os"
"os/user"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"unicode/utf8"
"github.com/acobaugh/osrelease"
"github.com/containers/toolbox/pkg/shell"
"github.com/docker/go-units"
"github.com/godbus/dbus/v5"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"golang.org/x/sys/unix"
)
type GetDefaultReleaseFunc func() (string, error)
type GetFullyQualifiedImageFunc func(string, string) string
type ParseReleaseFunc func(string) (string, error)
type Distro struct {
ContainerNamePrefix string
ImageBasename string
ReleaseRequired bool
GetDefaultRelease GetDefaultReleaseFunc
GetFullyQualifiedImage GetFullyQualifiedImageFunc
ParseRelease ParseReleaseFunc
}
type OptionValueSource int
const (
optionValueDefault OptionValueSource = iota
optionValueConfigFile
optionValueCLI
)
const (
containerNamePrefixFallback = "fedora-toolbox"
distroFallback = "fedora"
idTruncLength = 12
releaseFallback = "40"
)
const (
// Based on the nameRegex value in:
// https://github.com/containers/libpod/blob/master/libpod/options.go
ContainerNameRegexp = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"
)
var (
containerNamePrefixDefault string
distroDefault string
preservedEnvironmentVariables = []string{
"COLORTERM",
"CONTAINERS_STORAGE_CONF",
"DBUS_SESSION_BUS_ADDRESS",
"DBUS_SYSTEM_BUS_ADDRESS",
"DESKTOP_SESSION",
"DISPLAY",
"HISTCONTROL",
"HISTFILE",
"HISTFILESIZE",
"HISTIGNORE",
"HISTSIZE",
"HISTTIMEFORMAT",
"KONSOLE_VERSION",
"LANG",
"SHELL",
"SSH_AUTH_SOCK",
"TERM",
"TOOLBOX_PATH",
"USER",
"VTE_VERSION",
"WAYLAND_DISPLAY",
"XAUTHORITY",
"XDG_CURRENT_DESKTOP",
"XDG_DATA_DIRS",
"XDG_MENU_PREFIX",
"XDG_RUNTIME_DIR",
"XDG_SEAT",
"XDG_SESSION_CLASS",
"XDG_SESSION_DESKTOP",
"XDG_SESSION_ID",
"XDG_SESSION_TYPE",
"XDG_VTNR",
"XTERM_VERSION",
}
releaseDefault string
runtimeDirectories map[string]string
supportedDistros = map[string]Distro{
"arch": {
"arch-toolbox",
"arch-toolbox",
false,
getDefaultReleaseArch,
getFullyQualifiedImageArch,
parseReleaseArch,
},
"fedora": {
"fedora-toolbox",
"fedora-toolbox",
true,
getDefaultReleaseFedora,
getFullyQualifiedImageFedora,
parseReleaseFedora,
},
"rhel": {
"rhel-toolbox",
"toolbox",
true,
getDefaultReleaseRHEL,
getFullyQualifiedImageRHEL,
parseReleaseRHEL,
},
"ubuntu": {
"ubuntu-toolbox",
"ubuntu-toolbox",
true,
getDefaultReleaseUbuntu,
getFullyQualifiedImageUbuntu,
parseReleaseUbuntu,
},
}
)
var (
ContainerNameDefault string
ErrContainerNameFromImageInvalid = errors.New("container name generated from image is invalid")
ErrContainerNameInvalid = errors.New("container name is invalid")
ErrDistroUnsupported = errors.New("distribution is unsupported")
ErrDistroWithoutRelease = errors.New("non-default distribution must specify release")
ErrImageWithoutBasename = errors.New("image does not have a basename")
)
func init() {
containerNamePrefixDefault = containerNamePrefixFallback
distroDefault = distroFallback
releaseDefault = releaseFallback
hostID, err := GetHostID()
if err == nil {
if distroObj, supportedDistro := supportedDistros[hostID]; supportedDistro {
release, err := getDefaultReleaseForDistro(hostID)
if err == nil {
containerNamePrefixDefault = distroObj.ContainerNamePrefix
distroDefault = hostID
releaseDefault = release
}
}
}
ContainerNameDefault = containerNamePrefixDefault + "-" + releaseDefault
}
func CallFlatpakSessionHelper() (string, error) {
logrus.Debug("Calling org.freedesktop.Flatpak.SessionHelper.RequestSession")
connection, err := dbus.SessionBus()
if err != nil {
return "", errors.New("failed to connect to the D-Bus session instance")
}
sessionHelper := connection.Object("org.freedesktop.Flatpak", "/org/freedesktop/Flatpak/SessionHelper")
call := sessionHelper.Call("org.freedesktop.Flatpak.SessionHelper.RequestSession", 0)
var result map[string]dbus.Variant
err = call.Store(&result)
if err != nil {
logrus.Debugf("Call to org.freedesktop.Flatpak.SessionHelper.RequestSession failed: %s", err)
return "", errors.New("failed to call org.freedesktop.Flatpak.SessionHelper.RequestSession")
}
pathVariant := result["path"]
pathVariantSignature := pathVariant.Signature().String()
if pathVariantSignature != "s" {
return "", errors.New("unknown reply from org.freedesktop.Flatpak.SessionHelper.RequestSession")
}
pathValue := pathVariant.Value()
path := pathValue.(string)
return path, nil
}
func EnsureXdgRuntimeDirIsSet(uid int) {
if _, ok := os.LookupEnv("XDG_RUNTIME_DIR"); !ok {
logrus.Debug("XDG_RUNTIME_DIR is unset")
xdgRuntimeDir := fmt.Sprintf("/run/user/%d", uid)
os.Setenv("XDG_RUNTIME_DIR", xdgRuntimeDir)
logrus.Debugf("XDG_RUNTIME_DIR set to %s", xdgRuntimeDir)
}
}
func ForwardToHost() (int, error) {
envOptions := GetEnvOptionsForPreservedVariables()
toolboxPath := os.Getenv("TOOLBOX_PATH")
commandLineArgs := os.Args[1:]
var flatpakSpawnArgs []string
flatpakSpawnArgs = append(flatpakSpawnArgs, envOptions...)
flatpakSpawnArgs = append(flatpakSpawnArgs, []string{
"--host",
toolboxPath,
}...)
flatpakSpawnArgs = append(flatpakSpawnArgs, commandLineArgs...)
logrus.Debug("Forwarding to host:")
logrus.Debugf("%s", toolboxPath)
for _, arg := range commandLineArgs {
logrus.Debugf("%s", arg)
}
exitCode, err := shell.RunWithExitCode("flatpak-spawn", os.Stdin, os.Stdout, os.Stderr, flatpakSpawnArgs...)
if err != nil {
return exitCode, err
}
return exitCode, nil
}
// GetCgroupsVersion returns the cgroups version of the host
//
// Based on the IsCgroup2UnifiedMode function in:
// https://github.com/containers/libpod/tree/master/pkg/cgroups
func GetCgroupsVersion() (int, error) {
var st syscall.Statfs_t
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
return -1, err
}
version := 1
if st.Type == unix.CGROUP2_SUPER_MAGIC {
version = 2
}
return version, nil
}
func getContainerNamePrefixForImage(image string) (string, error) {
basename := ImageReferenceGetBasename(image)
if basename == "" {
return "", &ImageError{image, ErrImageWithoutBasename}
}
for _, distroObj := range supportedDistros {
if distroObj.ImageBasename != basename {
continue
}
return distroObj.ContainerNamePrefix, nil
}
return basename, nil
}
func getDefaultImageForDistro(distro, release string) string {
if distro == "" {
panic("distro not specified")
}
distroObj, supportedDistro := supportedDistros[distro]
if !supportedDistro {
panicMsg := fmt.Sprintf("failed to find %s in the list of supported distributions", distro)
panic(panicMsg)
}
image := distroObj.ImageBasename + ":" + release
return image
}
func getDefaultReleaseForDistro(distro string) (string, error) {
if distro == "" {
panic("distro not specified")
}
distroObj, supportedDistro := supportedDistros[distro]
if !supportedDistro {
panicMsg := fmt.Sprintf("failed to find %s in the list of supported distributions", distro)
panic(panicMsg)
}
release, err := distroObj.GetDefaultRelease()
if err != nil {
return "", err
}
return release, nil
}
func getDefaultReleaseRHEL() (string, error) {
release, err := getHostVersionID()
if err != nil {
return "", err
}
return release, nil
}
func getDefaultReleaseUbuntu() (string, error) {
release, err := getHostVersionID()
if err != nil {
return "", err
}
return release, nil
}
func GetEnvOptionsForPreservedVariables() []string {
logrus.Debug("Creating list of environment variables to forward")
var envOptions []string
for _, variable := range preservedEnvironmentVariables {
value, found := os.LookupEnv(variable)
if !found {
logrus.Debugf("%s is unset", variable)
continue
}
logrus.Debugf("%s=%s", variable, value)
envOptions = append(envOptions, fmt.Sprintf("--env=%s=%s", variable, value))
}
return envOptions
}
func GetFullyQualifiedImageFromDistros(image, release string) (string, error) {
logrus.Debugf("Resolving fully qualified name for image %s from known registries", image)
if image == "" {
panic("image not specified")
}
if release == "" {
panic("release not specified")
}
if tag := ImageReferenceGetTag(image); tag != "" && release != tag {
panicMsg := fmt.Sprintf("image %s does not match release %s", image, release)
panic(panicMsg)
}
if ImageReferenceHasDomain(image) {
return image, nil
}
basename := ImageReferenceGetBasename(image)
if basename == "" {
return "", fmt.Errorf("failed to get the basename of image %s", image)
}
for _, distroObj := range supportedDistros {
if distroObj.ImageBasename != basename {
continue
}
getFullyQualifiedImageImpl := distroObj.GetFullyQualifiedImage
imageFull := getFullyQualifiedImageImpl(image, release)
logrus.Debugf("Resolved image %s to %s", image, imageFull)
return imageFull, nil
}
return "", fmt.Errorf("failed to resolve image %s", image)
}
func getFullyQualifiedImageRHEL(image, release string) string {
i := strings.IndexRune(release, '.')
if i == -1 {
panicMsg := fmt.Sprintf("release %s not in '<major>.<minor>' format", release)
panic(panicMsg)
}
releaseMajor := release[:i]
imageFull := "registry.access.redhat.com/ubi" + releaseMajor + "/" + image
return imageFull
}
func getFullyQualifiedImageUbuntu(image, release string) string {
imageFull := "quay.io/toolbx/" + image
return imageFull
}
// GetGroupForSudo returns the name of the sudoers group.
//
// Some distros call it 'sudo' (eg. Ubuntu) and some call it 'wheel' (eg. Fedora).
func GetGroupForSudo() (string, error) {
logrus.Debug("Looking up group for sudo")
groups := []string{"sudo", "wheel"}
for _, group := range groups {
if _, err := user.LookupGroup(group); err == nil {
logrus.Debugf("Group for sudo is %s", group)
return group, nil
}
}
return "", errors.New("group for sudo not found")
}
// GetHostID returns the ID from the os-release files
//
// Examples:
// - host is Fedora, returned string is 'fedora'
func GetHostID() (string, error) {
osRelease, err := osrelease.Read()
if err != nil {
return "", err
}
return osRelease["ID"], nil
}
// GetHostVariantID returns the VARIANT_ID from the os-release files
//
// Examples:
// - host is Fedora Workstation, returned string is 'workstation'
func GetHostVariantID() (string, error) {
osRelease, err := osrelease.Read()
if err != nil {
return "", err
}
return osRelease["VARIANT_ID"], nil
}
// getHostVersionID returns the VERSION_ID from the os-release files
//
// Examples:
// - host is Fedora 32, returned string is '32'
func getHostVersionID() (string, error) {
osRelease, err := osrelease.Read()
if err != nil {
return "", err
}
return osRelease["VERSION_ID"], nil
}
func GetInitializedStamp(entryPointPID int, targetUser *user.User) (string, error) {
toolbxRuntimeDirectory, err := GetRuntimeDirectory(targetUser)
if err != nil {
return "", err
}
initializedStampBase := fmt.Sprintf("container-initialized-%d", entryPointPID)
initializedStamp := filepath.Join(toolbxRuntimeDirectory, initializedStampBase)
return initializedStamp, nil
}
// GetMountPoint returns the mount point of a target.
func GetMountPoint(target string) (string, error) {
var stdout strings.Builder
if err := shell.Run("df", nil, &stdout, nil, "--output=target", target); err != nil {
return "", err
}
output := stdout.String()
options := strings.Split(output, "\n")
if len(options) != 3 {
return "", errors.New("unexpected output from df(1)")
}
mountPoint := strings.TrimSpace(options[1])
return mountPoint, nil
}
// GetMountOptions returns the mount options of a target.
func GetMountOptions(target string) (string, error) {
var stdout strings.Builder
findMntArgs := []string{"--noheadings", "--output", "OPTIONS", target}
if err := shell.Run("findmnt", nil, &stdout, nil, findMntArgs...); err != nil {
return "", err
}
output := stdout.String()
options := strings.Split(output, "\n")
mountOptions := strings.TrimSpace(options[0])
return mountOptions, nil
}
func GetRuntimeDirectory(targetUser *user.User) (string, error) {
if runtimeDirectories == nil {
runtimeDirectories = make(map[string]string)
}
if toolboxRuntimeDirectory, ok := runtimeDirectories[targetUser.Uid]; ok {
return toolboxRuntimeDirectory, nil
}
gid, err := strconv.Atoi(targetUser.Gid)
if err != nil {
return "", fmt.Errorf("failed to convert group ID to integer: %w", err)
}
uid, err := strconv.Atoi(targetUser.Uid)
if err != nil {
return "", fmt.Errorf("failed to convert user ID to integer: %w", err)
}
var runtimeDirectory string
if uid == 0 {
runtimeDirectory = "/run"
} else {
runtimeDirectory = os.Getenv("XDG_RUNTIME_DIR")
}
toolboxRuntimeDirectory := path.Join(runtimeDirectory, "toolbox")
logrus.Debugf("Creating runtime directory %s", toolboxRuntimeDirectory)
if err := os.MkdirAll(toolboxRuntimeDirectory, 0700); err != nil {
return "", fmt.Errorf("failed to create runtime directory %s: %w", toolboxRuntimeDirectory, err)
}
if err := os.Chown(toolboxRuntimeDirectory, uid, gid); err != nil {
wrappedErr := fmt.Errorf("failed to change ownership of the runtime directory %s: %w",
toolboxRuntimeDirectory,
err)
return "", wrappedErr
}
runtimeDirectories[targetUser.Uid] = toolboxRuntimeDirectory
return toolboxRuntimeDirectory, nil
}
// GetSupportedDistros returns a list of supported distributions
func GetSupportedDistros() []string {
var distros []string
for d := range supportedDistros {
distros = append(distros, d)
}
return distros
}
// HumanDuration accepts a Unix time value and converts it into a human readable
// string.
//
// Examples: "5 minutes ago", "2 hours ago", "3 days ago"
func HumanDuration(duration int64) string {
return units.HumanDuration(time.Since(time.Unix(duration, 0))) + " ago"
}
// ImageReferenceCanBeID checks if 'image' might be the ID of an image
func ImageReferenceCanBeID(image string) bool {
matched, err := regexp.MatchString("^[a-f0-9]{6,64}$", image)
if err != nil {
panic("regular expression for ID reference matching is invalid")
}
return matched
}
func ImageReferenceGetBasename(image string) string {
var i int
if ImageReferenceHasDomain(image) {
i = strings.IndexRune(image, '/')
}
remainder := image[i:]
j := strings.IndexRune(remainder, ':')
if j == -1 {
j = len(remainder)
}
path := remainder[:j]
basename := filepath.Base(path)
return basename
}
func ImageReferenceGetDomain(image string) string {
if !ImageReferenceHasDomain(image) {
return ""
}
i := strings.IndexRune(image, '/')
domain := image[:i]
return domain
}
func ImageReferenceGetTag(image string) string {
var i int
if ImageReferenceHasDomain(image) {
i = strings.IndexRune(image, '/')
}
remainder := image[i:]
j := strings.IndexRune(remainder, ':')
if j == -1 {
return ""
}
tag := remainder[j+1:]
return tag
}
// ImageReferenceHasDomain checks if the provided image has a domain definition in it.
func ImageReferenceHasDomain(image string) bool {
i := strings.IndexRune(image, '/')
if i == -1 {
return false
}
prefix := image[:i]
// A domain should contain a top level domain name. An exception is 'localhost'
if !strings.ContainsAny(prefix, ".:") && prefix != "localhost" {
return false
}
return true
}
func SetUpConfiguration() error {
logrus.Debug("Setting up configuration")
configFiles := []string{
"/etc/containers/toolbox.conf",
}
userConfigDir, err := os.UserConfigDir()
if err != nil {
logrus.Debugf("Setting up configuration: failed to get the user config directory: %s", err)
return errors.New("failed to get the user config directory")
}
userConfigPath := userConfigDir + "/containers/toolbox.conf"
configFiles = append(configFiles, []string{
userConfigPath,
}...)
viper.SetConfigType("toml")
for _, configFile := range configFiles {
viper.SetConfigFile(configFile)
if err := viper.MergeInConfig(); err != nil {
var errConfigFileNotFound viper.ConfigFileNotFoundError
var errConfigParse viper.ConfigParseError
if errors.As(err, &errConfigFileNotFound) || errors.Is(err, os.ErrNotExist) {
logrus.Debugf("Setting up configuration: file %s not found", configFile)
continue
} else if errors.As(err, &errConfigParse) {
logrus.Debugf("Setting up configuration: failed to parse file %s: %s", configFile, err)
return fmt.Errorf("failed to parse file %s", configFile)
} else {
logrus.Debugf("Setting up configuration: failed to read file %s: %s", configFile, err)
return fmt.Errorf("failed to read file %s", configFile)
}
}
}
container, _, _, err := ResolveContainerAndImageNames("", "", "", "")
if err != nil {
logrus.Debugf("Setting up configuration: failed to resolve container name: %s", err)
return errors.New("failed to resolve container name")
}
ContainerNameDefault = container
return nil
}
// ShortID shortens provided id to first 12 characters.
func ShortID(id string) string {
if len(id) > idTruncLength {
return id[:idTruncLength]
}
return id
}
func parseRelease(distro, release string) (string, error) {
if distro == "" {
panic("distro not specified")
}
distroObj, supportedDistro := supportedDistros[distro]
if !supportedDistro {
panicMsg := fmt.Sprintf("failed to find %s in the list of supported distributions", distro)
panic(panicMsg)
}
parseReleaseImpl := distroObj.ParseRelease
release, err := parseReleaseImpl(release)
return release, err
}
func parseReleaseRHEL(release string) (string, error) {
if i := strings.IndexRune(release, '.'); i == -1 {
return "", &ParseReleaseError{"The release must be in the '<major>.<minor>' format."}
}
releaseN, err := strconv.ParseFloat(release, 32)
if err != nil {
logrus.Debugf("Parsing release %s as a float failed: %s", release, err)
return "", &ParseReleaseError{"The release must be in the '<major>.<minor>' format."}
}
if releaseN <= 0 {
return "", &ParseReleaseError{"The release must be a positive number."}
}
return release, nil
}
func parseReleaseUbuntu(release string) (string, error) {
releaseParts := strings.Split(release, ".")
if len(releaseParts) != 2 {
return "", &ParseReleaseError{"The release must be in the 'YY.MM' format."}
}
releaseYear, err := strconv.Atoi(releaseParts[0])
if err != nil {
logrus.Debugf("Parsing release year %s as an integer failed: %s", releaseParts[0], err)
return "", &ParseReleaseError{"The release must be in the 'YY.MM' format."}
}
if releaseYear < 4 {
return "", &ParseReleaseError{"The release year must be 4 or more."}
}
releaseYearLen := utf8.RuneCountInString(releaseParts[0])
if releaseYearLen > 2 {
return "", &ParseReleaseError{"The release year cannot have more than two digits."}
} else if releaseYear < 10 && releaseYearLen == 2 {
return "", &ParseReleaseError{"The release year cannot have a leading zero."}
}
releaseMonth, err := strconv.Atoi(releaseParts[1])
if err != nil {
logrus.Debugf("Parsing release month %s as an integer failed: %s", releaseParts[1], err)
return "", &ParseReleaseError{"The release must be in the 'YY.MM' format."}
}
if releaseMonth < 1 {
return "", &ParseReleaseError{"The release month must be between 01 and 12."}
} else if releaseMonth > 12 {
return "", &ParseReleaseError{"The release month must be between 01 and 12."}
}
releaseMonthLen := utf8.RuneCountInString(releaseParts[1])
if releaseMonthLen != 2 {
return "", &ParseReleaseError{"The release month must have two digits."}
}
return release, nil
}
// PathExists wraps around os.Stat providing a nice interface for checking an existence of a path.
func PathExists(path string) bool {
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
return true
}
return false
}
// IsContainerNameValid checks if the name of a container matches the right pattern
func IsContainerNameValid(containerName string) bool {
pattern := "^" + ContainerNameRegexp + "$"
matched, err := regexp.MatchString(pattern, containerName)
if err != nil {
panicMsg := fmt.Sprintf("failed to parse regular expression for container name: %v", err)
panic(panicMsg)
}
return matched
}
func IsInsideContainer() bool {
return PathExists("/run/.containerenv")
}
func IsInsideToolboxContainer() bool {
return PathExists("/run/.toolboxenv")
}
// ResolveContainerAndImageNames takes care of standardizing names of containers and images.
//
// If no image name is specified then the base image will reflect the platform of the host (even the version).
// If no container name is specified then the name of the image will be used.
//
// If the host system is unknown then the base image will be 'fedora-toolbox' with a default version
func ResolveContainerAndImageNames(container, distroCLI, imageCLI, releaseCLI string) (string, string, string, error) {
logrus.Debug("Resolving container and image names")
logrus.Debugf("Container: '%s'", container)
logrus.Debugf("Distribution (CLI): '%s'", distroCLI)
logrus.Debugf("Image (CLI): '%s'", imageCLI)
logrus.Debugf("Release (CLI): '%s'", releaseCLI)
distro, distroSource := distroCLI, optionValueCLI
image, release := imageCLI, releaseCLI
if distroCLI == "" {
distro, distroSource = distroDefault, optionValueDefault
if viper.IsSet("general.distro") {
distro, distroSource = viper.GetString("general.distro"), optionValueConfigFile
}
}
distroObj, supportedDistro := supportedDistros[distro]
if !supportedDistro {
return "", "", "", &DistroError{distro, ErrDistroUnsupported}
}
if distro == distroDefault {
if releaseCLI == "" {
release = releaseDefault
if viper.IsSet("general.release") {
release = viper.GetString("general.release")
}
}
} else {
if distroObj.ReleaseRequired {
if releaseCLI == "" && !viper.IsSet("general.release") {
return "", "", "", &DistroError{distro, ErrDistroWithoutRelease}
}
if releaseCLI == "" {
release = viper.GetString("general.release")
}
if release == "" {
panicMsg := fmt.Sprintf("cannot find release for non-default distribution %s", distro)
panic(panicMsg)
}
} else {
switch distroSource {
case optionValueCLI:
// 'release' already set to 'releaseCLI'
case optionValueConfigFile:
if releaseCLI == "" {
if viper.IsSet("general.release") {
release = viper.GetString("general.release")
}
}
case optionValueDefault:
panic("distro must be non-default")
default:
panic("cannot handle new OptionValueSource")
}
}
}
release, err := parseRelease(distro, release)
if err != nil {
return "", "", "", err
}
if imageCLI == "" {
image = getDefaultImageForDistro(distro, release)
if viper.IsSet("general.image") && distroCLI == "" && releaseCLI == "" {
image = viper.GetString("general.image")
release = ImageReferenceGetTag(image)
if release == "" {
release = releaseDefault
if viper.IsSet("general.release") {
release = viper.GetString("general.release")
}
}
}
} else {
release = ImageReferenceGetTag(image)
if release == "" {
release = releaseDefault
if viper.IsSet("general.release") {
release = viper.GetString("general.release")
}
}
}
if container == "" {
var err error
container, err = getContainerNamePrefixForImage(image)
if err != nil {
return "", "", "", err
}
tag := ImageReferenceGetTag(image)
if tag != "" {
container = container + "-" + tag
}
if !IsContainerNameValid(container) {
return "", "", "", &ContainerError{container, image, ErrContainerNameFromImageInvalid}
}
} else {
if !IsContainerNameValid(container) {
return "", "", "", &ContainerError{container, "", ErrContainerNameInvalid}
}
}
logrus.Debug("Resolved container and image names")
logrus.Debugf("Container: '%s'", container)
logrus.Debugf("Image: '%s'", image)
logrus.Debugf("Release: '%s'", release)
return container, image, release, nil
}