mirror of
https://github.com/openshift/source-to-image.git
synced 2026-02-06 06:44:58 +01:00
Adding check to ensure the s2i assemble user is allowed if the --allowed-uids flag is set. The assemble user can come from one of two sources: 1. --assemble-user flag 2. builder image io.openshift.s2i.assemble-user label The assemble user overrides the default image user for an s2i build. However, if the base image has ONBUILD instructions with USER directives, all USER directives will be checked to ensure compliance. Bug 1582976
305 lines
9.9 KiB
Go
305 lines
9.9 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
utilglog "github.com/openshift/source-to-image/pkg/util/glog"
|
|
)
|
|
|
|
// Common S2I errors
|
|
const (
|
|
InspectImageError int = 1 + iota
|
|
PullImageError
|
|
SaveArtifactsError
|
|
AssembleError
|
|
WorkdirError
|
|
BuildError
|
|
TarTimeoutError
|
|
DownloadError
|
|
ScriptsInsideImageError
|
|
InstallError
|
|
InstallErrorRequired
|
|
URLHandlerError
|
|
STIContainerError
|
|
SourcePathError
|
|
UserNotAllowedError
|
|
EmptyGitRepositoryError
|
|
)
|
|
|
|
// Error represents an error thrown during S2I execution
|
|
type Error struct {
|
|
Message string
|
|
Details error
|
|
ErrorCode int
|
|
Suggestion string
|
|
}
|
|
|
|
// ContainerError is an error returned when a container exits with a non-zero code.
|
|
// ExitCode contains the exit code from the container
|
|
type ContainerError struct {
|
|
Message string
|
|
Output string
|
|
ErrorCode int
|
|
Suggestion string
|
|
ExitCode int
|
|
}
|
|
|
|
// Error returns a string for a given error
|
|
func (s Error) Error() string {
|
|
return s.Message
|
|
}
|
|
|
|
// Error returns a string for the given error
|
|
func (s ContainerError) Error() string {
|
|
return s.Message
|
|
}
|
|
|
|
// NewInspectImageError returns a new error which indicates there was a problem
|
|
// inspecting the image
|
|
func NewInspectImageError(name string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("unable to get metadata for %s", name),
|
|
Details: err,
|
|
ErrorCode: InspectImageError,
|
|
Suggestion: "check image name",
|
|
}
|
|
}
|
|
|
|
// NewPullImageError returns a new error which indicates there was a problem
|
|
// pulling the image
|
|
func NewPullImageError(name string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("unable to get %s", name),
|
|
Details: err,
|
|
ErrorCode: PullImageError,
|
|
Suggestion: "check image name, or if using local image add --pull-policy=never flag",
|
|
}
|
|
}
|
|
|
|
// NewSaveArtifactsError returns a new error which indicates there was a problem
|
|
// calling save-artifacts script
|
|
func NewSaveArtifactsError(name, output string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("saving artifacts for %s failed:\n%s", name, output),
|
|
Details: err,
|
|
ErrorCode: SaveArtifactsError,
|
|
Suggestion: "check the save-artifacts script for errors",
|
|
}
|
|
}
|
|
|
|
// NewAssembleError returns a new error which indicates there was a problem
|
|
// running assemble script
|
|
func NewAssembleError(name, output string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("assemble for %s failed:\n%s", name, output),
|
|
Details: err,
|
|
ErrorCode: AssembleError,
|
|
Suggestion: "check the assemble script output for errors",
|
|
}
|
|
}
|
|
|
|
// NewWorkDirError returns a new error which indicates there was a problem
|
|
// when creating working directory
|
|
func NewWorkDirError(dir string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("creating temporary directory %s failed", dir),
|
|
Details: err,
|
|
ErrorCode: WorkdirError,
|
|
Suggestion: "check if you have access to your system's temporary directory",
|
|
}
|
|
}
|
|
|
|
// NewBuildError returns a new error which indicates there was a problem
|
|
// building the image
|
|
func NewBuildError(name string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("building %s failed", name),
|
|
Details: err,
|
|
ErrorCode: BuildError,
|
|
Suggestion: "check the build output for errors",
|
|
}
|
|
}
|
|
|
|
// NewCommitError returns a new error which indicates there was a problem
|
|
// committing the image
|
|
func NewCommitError(name string, err error) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("building %s failed when committing the image due to error: %v", name, err),
|
|
Details: err,
|
|
ErrorCode: BuildError,
|
|
Suggestion: "check the build output for errors",
|
|
}
|
|
}
|
|
|
|
// NewTarTimeoutError returns a new error which indicates there was a problem
|
|
// when sending or receiving tar stream
|
|
func NewTarTimeoutError() error {
|
|
return Error{
|
|
Message: fmt.Sprintf("timeout waiting for tar stream"),
|
|
Details: nil,
|
|
ErrorCode: TarTimeoutError,
|
|
Suggestion: "check the Source-To-Image scripts if it accepts tar stream for assemble and sends for save-artifacts",
|
|
}
|
|
}
|
|
|
|
// NewDownloadError returns a new error which indicates there was a problem
|
|
// when downloading a file
|
|
func NewDownloadError(url string, code int) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("failed to retrieve %s, response code %d", url, code),
|
|
Details: nil,
|
|
ErrorCode: DownloadError,
|
|
Suggestion: "check the availability of the address",
|
|
}
|
|
}
|
|
|
|
// NewScriptsInsideImageError returns a new error which informs of scripts
|
|
// being placed inside the image
|
|
func NewScriptsInsideImageError(url string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("scripts inside the image: %s", url),
|
|
Details: nil,
|
|
ErrorCode: ScriptsInsideImageError,
|
|
Suggestion: "",
|
|
}
|
|
}
|
|
|
|
// NewInstallError returns a new error which indicates there was a problem
|
|
// when downloading a script
|
|
func NewInstallError(script string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("failed to install %v", script),
|
|
Details: nil,
|
|
ErrorCode: InstallError,
|
|
Suggestion: "provide URL with Source-To-Image scripts with -s flag or check the image if it contains %q label set",
|
|
}
|
|
}
|
|
|
|
// NewInstallRequiredError returns a new error which indicates there was a problem
|
|
// when downloading a required script
|
|
func NewInstallRequiredError(scripts []string, label string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("failed to install %v", scripts),
|
|
Details: nil,
|
|
ErrorCode: InstallErrorRequired,
|
|
Suggestion: "provide URL with Source-To-Image scripts with -s flag or check the image if it contains " + label + " label set",
|
|
}
|
|
}
|
|
|
|
// NewURLHandlerError returns a new error which indicates there was a problem
|
|
// when trying to read scripts URL
|
|
func NewURLHandlerError(url string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("no URL handler for %s", url),
|
|
Details: nil,
|
|
ErrorCode: URLHandlerError,
|
|
Suggestion: "check the URL",
|
|
}
|
|
}
|
|
|
|
// NewContainerError return a new error which indicates there was a problem
|
|
// invoking command inside container
|
|
func NewContainerError(name string, code int, output string) error {
|
|
return ContainerError{
|
|
Message: fmt.Sprintf("non-zero (%d) exit code from %s", code, name),
|
|
Output: output,
|
|
ErrorCode: STIContainerError,
|
|
Suggestion: "check the container logs for more information on the failure",
|
|
ExitCode: code,
|
|
}
|
|
}
|
|
|
|
// NewSourcePathError returns a new error which indicates there was a problem
|
|
// when accessing the source code from the local filesystem
|
|
func NewSourcePathError(path string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("Local filesystem source path does not exist: %s", path),
|
|
Details: nil,
|
|
ErrorCode: SourcePathError,
|
|
Suggestion: "check the source code path on the local filesystem",
|
|
}
|
|
}
|
|
|
|
// NewUserNotAllowedError returns a new error that indicates that the build
|
|
// could not run because the image uses a user outside of the range of allowed users
|
|
func NewUserNotAllowedError(image string, onbuild bool) error {
|
|
var msg string
|
|
if onbuild {
|
|
msg = fmt.Sprintf("image %q includes at least one ONBUILD instruction that sets the user to a user that is not allowed", image)
|
|
} else {
|
|
msg = fmt.Sprintf("image %q must specify a user that is numeric and within the range of allowed users", image)
|
|
}
|
|
return Error{
|
|
Message: msg,
|
|
ErrorCode: UserNotAllowedError,
|
|
Suggestion: fmt.Sprintf("modify image %q to use a numeric user within the allowed range, or build without the --allowed-uids flag", image),
|
|
}
|
|
}
|
|
|
|
// NewAssembleUserNotAllowedError returns a new error that indicates that the build
|
|
// could not run because the build or image uses an assemble user outside of the range
|
|
// of allowed users.
|
|
func NewAssembleUserNotAllowedError(image string, usesConfig bool) error {
|
|
var msg, suggestion string
|
|
if usesConfig {
|
|
msg = "assemble user must be numeric and within the range of allowed users"
|
|
suggestion = "build without the allowed-uids or assemble-user configurations set"
|
|
} else {
|
|
assembleLabel := "io.openshift.s2i.assemble-user"
|
|
msg = fmt.Sprintf("image %q includes the %q label whose value is not within the allowed range", image, assembleLabel)
|
|
suggestion = fmt.Sprintf("modify the %q label in image %q to use a numeric user within the allowed range, or build without the allowed-uids configuration set", assembleLabel, image)
|
|
}
|
|
return Error{
|
|
Message: msg,
|
|
ErrorCode: UserNotAllowedError,
|
|
Suggestion: suggestion,
|
|
}
|
|
}
|
|
|
|
// NewEmptyGitRepositoryError returns a new error which indicates that a found
|
|
// .git directory has no tracking information, e.g. if the user simply used
|
|
// `git init` and forgot about the repository
|
|
func NewEmptyGitRepositoryError(source string) error {
|
|
return Error{
|
|
Message: fmt.Sprintf("The git repository \"%s\" has no tracking information or commits", source),
|
|
ErrorCode: EmptyGitRepositoryError,
|
|
Suggestion: "Either commit files to the Git repository, remove the .git directory from the project, or use --copy to ignore the repository.",
|
|
}
|
|
}
|
|
|
|
// glog is a placeholder until the builders pass an output stream down
|
|
// client facing libraries should not be using glog
|
|
var glog = utilglog.StderrLog
|
|
|
|
// CheckError checks input error.
|
|
// 1. if the input error is nil, the function does nothing but return.
|
|
// 2. if the input error is a kind of Error which is thrown during S2I execution,
|
|
// the function handle it with Suggestion and Details.
|
|
// 3. if the input error is a kind of system Error which is unknown, the function exit with 1.
|
|
func CheckError(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
if e, ok := err.(Error); ok {
|
|
glog.Errorf("An error occurred: %v", e)
|
|
glog.Errorf("Suggested solution: %v", e.Suggestion)
|
|
if e.Details != nil {
|
|
glog.V(1).Infof("Details: %v", e.Details)
|
|
}
|
|
glog.Error("If the problem persists consult the docs at https://github.com/openshift/source-to-image/tree/master/docs. " +
|
|
"Eventually reach us on freenode #openshift or file an issue at https://github.com/openshift/source-to-image/issues " +
|
|
"providing us with a log from your build using --loglevel=3")
|
|
os.Exit(e.ErrorCode)
|
|
} else {
|
|
glog.Errorf("An error occurred: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// UsageError checks command usage error.
|
|
func UsageError(msg string) error {
|
|
return fmt.Errorf("%s", msg)
|
|
}
|