toolbox-0.0.99.2^1.git660b6970e998-1.el9
- Add support for configuration files - Related: #1970747 Signed-off-by: Jindrich Novy <jnovy@redhat.com>
This commit is contained in:
parent
b817729eef
commit
7ebedec3a3
2
sources
2
sources
@ -1 +1 @@
|
||||
SHA512 (toolbox-0.0.99.2-vendored.tar.xz) = 44eab6eb98fc8e52e03fc753e1c26e5d2d8a91e5ba84817bbf76f7dc6601581b5922115c140637951074ab8d741f443b3217a07639ea4d7bdf81d5faf836e3b2
|
||||
SHA512 (toolbox-0.0.99.2^1.git660b6970e998-vendored.tar.xz) = edc035b19e8330c55669ad318da1553a700c6e064ba602fe2e7da83f6db23ff1a13297f91fb816b6cc58cbccf1676125923f1c889df86edde60374fdec1d8f6f
|
||||
|
@ -1,667 +0,0 @@
|
||||
From 62a022428231df50637495673354d4fdd2541c1a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Mon, 7 Jun 2021 14:11:27 +0200
|
||||
Subject: [PATCH 1/7] pkg/podman: Support parsing 'podman pull' spew into a Go
|
||||
error
|
||||
|
||||
This is meant to get a better understanding of a failed 'podman pull'
|
||||
invocation to understand whether pulling an image requires logging into
|
||||
the registry or not. Currently, 'podman pull' doesn't have a dedicated
|
||||
exit code to denote authorization errors, so this is meant to be a
|
||||
temporary workaround for that.
|
||||
|
||||
Parsing the error stream is inherently fragile and tricky because
|
||||
there's no guarantee that the structure of the messages won't change,
|
||||
and there's no clear definition of how the messages are laid out.
|
||||
Therefore, this approach can't be treated as a generic solution for
|
||||
getting detailed information about failed Podman invocations.
|
||||
|
||||
The error stream is used not only for dumping error messages, but also
|
||||
for showing progress bars. Therefore, all lines are skipped until one
|
||||
that starts with "Error: " is found. This is a heuristic based on how
|
||||
Go programs written using the Cobra [1] library tend to report errors.
|
||||
|
||||
All subsequent lines are taken together and split around the ": "
|
||||
sub-string, on the assumption that the ": " sub-string is used when a
|
||||
new error message is prefixed to an inner error. Each sub-string
|
||||
created from the split is treated as a potential member of the chain of
|
||||
errors reported within Podman.
|
||||
|
||||
Some real world examples of the 'podman pull' error stream in the case
|
||||
of authorization errors are:
|
||||
|
||||
* With Docker Hub (https://hub.docker.com/):
|
||||
Trying to pull docker.io/library/foobar:latest...
|
||||
Error: Error initializing source docker://foobar:latest: Error reading manifest latest in docker.io/library/foobar: errors:
|
||||
denied: requested access to the resource is denied
|
||||
unauthorized: authentication required
|
||||
|
||||
* With registry.redhat.io:
|
||||
Trying to pull registry.redhat.io/foobar:latest...
|
||||
Error: Error initializing source docker://registry.redhat.io/foobar:latest: unable to retrieve auth token: invalid username/password: unauthorized: Please login to the Red Hat Registry using your Customer Portal credentials. Further instructions can be found here: https://access.redhat.com/RegistryAuthentication
|
||||
|
||||
[1] https://github.com/spf13/cobra/
|
||||
https://pkg.go.dev/github.com/spf13/cobra
|
||||
|
||||
https://github.com/containers/toolbox/pull/786
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/pkg/podman/error.go | 109 +++++++++++++++++++++++++
|
||||
src/pkg/podman/error_test.go | 152 +++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 261 insertions(+)
|
||||
create mode 100644 src/pkg/podman/error.go
|
||||
create mode 100644 src/pkg/podman/error_test.go
|
||||
|
||||
diff --git a/src/pkg/podman/error.go b/src/pkg/podman/error.go
|
||||
new file mode 100644
|
||||
index 000000000000..ea35de8d7ed8
|
||||
--- /dev/null
|
||||
+++ b/src/pkg/podman/error.go
|
||||
@@ -0,0 +1,109 @@
|
||||
+/*
|
||||
+ * Copyright © 2021 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 podman
|
||||
+
|
||||
+import (
|
||||
+ "bufio"
|
||||
+ "bytes"
|
||||
+ "strings"
|
||||
+)
|
||||
+
|
||||
+// internalError serves for representing errors printed by Podman to stderr
|
||||
+type internalError struct {
|
||||
+ errors []string
|
||||
+}
|
||||
+
|
||||
+func (e *internalError) Error() string {
|
||||
+ if e.errors == nil || len(e.errors) == 0 {
|
||||
+ return ""
|
||||
+ }
|
||||
+
|
||||
+ var builder strings.Builder
|
||||
+
|
||||
+ for i, part := range e.errors {
|
||||
+ if i != 0 {
|
||||
+ builder.WriteString(": ")
|
||||
+ }
|
||||
+
|
||||
+ builder.WriteString(part)
|
||||
+ }
|
||||
+
|
||||
+ return builder.String()
|
||||
+}
|
||||
+
|
||||
+// Is lexically compares errors
|
||||
+//
|
||||
+// The comparison is done for every part in the error chain not across.
|
||||
+func (e *internalError) Is(target error) bool {
|
||||
+ if target == nil {
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
+ if e.errors == nil || len(e.errors) == 0 {
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
+ for _, part := range e.errors {
|
||||
+ if part == target.Error() {
|
||||
+ return true
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false
|
||||
+}
|
||||
+
|
||||
+func (e *internalError) Unwrap() error {
|
||||
+ if e.errors == nil || len(e.errors) <= 1 {
|
||||
+ return nil
|
||||
+ }
|
||||
+
|
||||
+ return &internalError{e.errors[1:]}
|
||||
+}
|
||||
+
|
||||
+// parseErrorMsg serves for converting error output of Podman into an error
|
||||
+// that can be further used in Go
|
||||
+func parseErrorMsg(stderr *bytes.Buffer) error {
|
||||
+ // Stderr is not used only for error messages but also for things like
|
||||
+ // progress bars. We're only interested in the error messages.
|
||||
+
|
||||
+ var errMsgFound bool
|
||||
+ var errMsgParts []string
|
||||
+
|
||||
+ scanner := bufio.NewScanner(stderr)
|
||||
+ for scanner.Scan() {
|
||||
+ line := scanner.Text()
|
||||
+
|
||||
+ if strings.HasPrefix(line, "Error: ") {
|
||||
+ line = strings.TrimPrefix(line, "Error: ")
|
||||
+ errMsgFound = true
|
||||
+ }
|
||||
+
|
||||
+ if errMsgFound {
|
||||
+ line = strings.TrimSpace(line)
|
||||
+ line = strings.Trim(line, ":")
|
||||
+
|
||||
+ parts := strings.Split(line, ": ")
|
||||
+ errMsgParts = append(errMsgParts, parts...)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if !errMsgFound {
|
||||
+ return nil
|
||||
+ }
|
||||
+
|
||||
+ return &internalError{errMsgParts}
|
||||
+}
|
||||
diff --git a/src/pkg/podman/error_test.go b/src/pkg/podman/error_test.go
|
||||
new file mode 100644
|
||||
index 000000000000..11652c709bdd
|
||||
--- /dev/null
|
||||
+++ b/src/pkg/podman/error_test.go
|
||||
@@ -0,0 +1,152 @@
|
||||
+/*
|
||||
+ * Copyright © 2021 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 podman
|
||||
+
|
||||
+import (
|
||||
+ "bytes"
|
||||
+ "errors"
|
||||
+ "testing"
|
||||
+
|
||||
+ "github.com/stretchr/testify/assert"
|
||||
+)
|
||||
+
|
||||
+func TestInternalError(t *testing.T) {
|
||||
+ type expect struct {
|
||||
+ IsNil bool
|
||||
+ Error string
|
||||
+ Search string
|
||||
+ Wrap []string
|
||||
+ }
|
||||
+
|
||||
+ testCases := []struct {
|
||||
+ name string
|
||||
+ input string
|
||||
+ expect expect
|
||||
+ }{
|
||||
+ {
|
||||
+ name: "Empty",
|
||||
+ input: "",
|
||||
+ expect: expect{
|
||||
+ IsNil: true,
|
||||
+ Error: "",
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Text with only a prolog and no error message",
|
||||
+ input: "There is only a prolog and no error message",
|
||||
+ expect: expect{
|
||||
+ IsNil: true,
|
||||
+ Error: "",
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Text with only a prolog and no error message",
|
||||
+ input: "There is only a prolog Error: not an error message",
|
||||
+ expect: expect{
|
||||
+ IsNil: true,
|
||||
+ Error: "",
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Text with a prolog before the error message",
|
||||
+ input: `There is a prolog
|
||||
+Error: an error message`,
|
||||
+ expect: expect{
|
||||
+ Error: "an error message",
|
||||
+ Search: "an error message",
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Error message with several wrapped errors",
|
||||
+ input: "Error: level 1: level 2: level 3: level 4",
|
||||
+ expect: expect{
|
||||
+ Error: "level 1: level 2: level 3: level 4",
|
||||
+ Search: "level 4",
|
||||
+ Wrap: []string{"level 1", "level 2", "level 3", "level 4"},
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Error message with a bullet list",
|
||||
+ input: `Error: an error message:
|
||||
+ err1
|
||||
+ err2
|
||||
+ err3`,
|
||||
+ expect: expect{
|
||||
+ Error: "an error message: err1: err2: err3",
|
||||
+ Search: "err2",
|
||||
+ Wrap: []string{"an error message", "err1", "err2", "err3"},
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Error message from 'podman pull' - unauthorized (Docker Hub)",
|
||||
+ input: `Trying to pull docker.io/library/foobar:latest...
|
||||
+Error: Error initializing source docker://foobar:latest: Error reading manifest latest in docker.io/library/foobar: errors:
|
||||
+denied: requested access to the resource is denied
|
||||
+unauthorized: authentication required`,
|
||||
+ expect: expect{
|
||||
+ Error: "Error initializing source docker://foobar:latest: Error reading manifest latest in docker.io/library/foobar: errors: denied: requested access to the resource is denied: unauthorized: authentication required",
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ name: "Error message from 'podman pull' - unauthorized (Red Hat Registry)",
|
||||
+ input: `Trying to pull registry.redhat.io/foobar:latest...
|
||||
+Error: Error initializing source docker://registry.redhat.io/foobar:latest: unable to retrieve auth token: invalid username/password: unauthorized: Please login to the Red Hat Registry using your Customer Portal credentials. Further instructions can be found here: https://access.redhat.com/RegistryAuthentication
|
||||
+`,
|
||||
+ expect: expect{
|
||||
+ Error: "Error initializing source docker://registry.redhat.io/foobar:latest: unable to retrieve auth token: invalid username/password: unauthorized: Please login to the Red Hat Registry using your Customer Portal credentials. Further instructions can be found here: https://access.redhat.com/RegistryAuthentication",
|
||||
+ },
|
||||
+ },
|
||||
+ }
|
||||
+
|
||||
+ for _, tc := range testCases {
|
||||
+ t.Run(tc.name, func(t *testing.T) {
|
||||
+ err := parseErrorMsg(bytes.NewBufferString(tc.input))
|
||||
+
|
||||
+ if tc.expect.IsNil {
|
||||
+ assert.Nil(t, err)
|
||||
+ return
|
||||
+ } else {
|
||||
+ assert.NotNil(t, err)
|
||||
+ }
|
||||
+
|
||||
+ errInternal := err.(*internalError)
|
||||
+ assert.Equal(t, tc.expect.Error, errInternal.Error())
|
||||
+
|
||||
+ if tc.expect.Search != "" {
|
||||
+ assert.True(t, errInternal.Is(errors.New(tc.expect.Search)))
|
||||
+ }
|
||||
+
|
||||
+ if len(tc.expect.Wrap) != 0 {
|
||||
+ for {
|
||||
+ assert.Equal(t, len(tc.expect.Wrap), len(errInternal.errors))
|
||||
+
|
||||
+ for i, part := range tc.expect.Wrap {
|
||||
+ assert.Equal(t, part, errInternal.errors[i])
|
||||
+ }
|
||||
+
|
||||
+ err = errInternal.Unwrap()
|
||||
+ if err == nil {
|
||||
+ assert.Equal(t, len(tc.expect.Wrap), 1)
|
||||
+ break
|
||||
+ }
|
||||
+ errInternal = err.(*internalError)
|
||||
+ tc.expect.Wrap = tc.expect.Wrap[1:]
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From 146c93b431941d21b3c686734a7b83a737072004 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sat, 3 Jul 2021 22:06:30 +0200
|
||||
Subject: [PATCH 2/7] pkg/shell: Allow extracting the stderr even when it's
|
||||
shown to the user
|
||||
|
||||
This way the standard error stream of the spawned binaries can be
|
||||
inspected to get a better understanding of the failure, while still
|
||||
being shown to the user when run with the '--verbose' flag.
|
||||
|
||||
Unfortunately, this breaks the progress bar in 'podman pull' because
|
||||
the standard error stream is no longer connected to a file descriptor
|
||||
that's a terminal device.
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
https://github.com/containers/toolbox/pull/823
|
||||
---
|
||||
src/pkg/shell/shell.go | 22 +++++++++++++++++-----
|
||||
1 file changed, 17 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/src/pkg/shell/shell.go b/src/pkg/shell/shell.go
|
||||
index 272dcc9ca693..c63b85bc8745 100644
|
||||
--- a/src/pkg/shell/shell.go
|
||||
+++ b/src/pkg/shell/shell.go
|
||||
@@ -40,15 +40,12 @@ func Run(name string, stdin io.Reader, stdout, stderr io.Writer, arg ...string)
|
||||
}
|
||||
|
||||
func RunWithExitCode(name string, stdin io.Reader, stdout, stderr io.Writer, arg ...string) (int, error) {
|
||||
- logLevel := logrus.GetLevel()
|
||||
- if stderr == nil && logLevel >= logrus.DebugLevel {
|
||||
- stderr = os.Stderr
|
||||
- }
|
||||
+ stderrWrapper := getStderrWrapper(stderr)
|
||||
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Stdin = stdin
|
||||
cmd.Stdout = stdout
|
||||
- cmd.Stderr = stderr
|
||||
+ cmd.Stderr = stderrWrapper
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
@@ -66,3 +63,18 @@ func RunWithExitCode(name string, stdin io.Reader, stdout, stderr io.Writer, arg
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
+
|
||||
+func getStderrWrapper(buffer io.Writer) io.Writer {
|
||||
+ var stderr io.Writer
|
||||
+
|
||||
+ logLevel := logrus.GetLevel()
|
||||
+ if logLevel < logrus.DebugLevel {
|
||||
+ stderr = buffer
|
||||
+ } else if buffer == nil {
|
||||
+ stderr = os.Stderr
|
||||
+ } else {
|
||||
+ stderr = io.MultiWriter(buffer, os.Stderr)
|
||||
+ }
|
||||
+
|
||||
+ return stderr
|
||||
+}
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From 5c3249033224c6f107a2d39ab84e519303d74072 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sun, 4 Jul 2021 18:15:16 +0200
|
||||
Subject: [PATCH 3/7] pkg/podman: Try to parse the error from 'podman pull'
|
||||
into a Go error
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/pkg/podman/podman.go | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go
|
||||
index 9099df1eaf2a..8e7d5068fb97 100644
|
||||
--- a/src/pkg/podman/podman.go
|
||||
+++ b/src/pkg/podman/podman.go
|
||||
@@ -228,10 +228,13 @@ func IsToolboxImage(image string) (bool, error) {
|
||||
|
||||
// Pull pulls an image
|
||||
func Pull(imageName string) error {
|
||||
+ var stderr bytes.Buffer
|
||||
+
|
||||
logLevelString := LogLevel.String()
|
||||
args := []string{"--log-level", logLevelString, "pull", imageName}
|
||||
|
||||
- if err := shell.Run("podman", nil, nil, nil, args...); err != nil {
|
||||
+ if err := shell.Run("podman", nil, nil, &stderr, args...); err != nil {
|
||||
+ err := parseErrorMsg(&stderr)
|
||||
return err
|
||||
}
|
||||
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From 4b8b007201cd019909829d73afdcf919753943a3 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sun, 4 Jul 2021 18:16:12 +0200
|
||||
Subject: [PATCH 4/7] pkg/podman: Add error for 'podman pull' failing due to no
|
||||
authorization
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/pkg/podman/podman.go | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go
|
||||
index 8e7d5068fb97..521538c9abfb 100644
|
||||
--- a/src/pkg/podman/podman.go
|
||||
+++ b/src/pkg/podman/podman.go
|
||||
@@ -19,6 +19,7 @@ package podman
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -32,7 +33,8 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
- LogLevel = logrus.ErrorLevel
|
||||
+ ErrUnauthorized = errors.New("unauthorized")
|
||||
+ LogLevel = logrus.ErrorLevel
|
||||
)
|
||||
|
||||
// CheckVersion compares provided version with the version of Podman.
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From ef3bebac89cd833a663dc63fffba8b04ca944dde Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sat, 3 Jul 2021 02:25:49 +0200
|
||||
Subject: [PATCH 5/7] pkg/podman: Wrap 'podman login'
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/pkg/podman/podman.go | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
diff --git a/src/pkg/podman/podman.go b/src/pkg/podman/podman.go
|
||||
index 521538c9abfb..8538bd7c617a 100644
|
||||
--- a/src/pkg/podman/podman.go
|
||||
+++ b/src/pkg/podman/podman.go
|
||||
@@ -228,6 +228,18 @@ func IsToolboxImage(image string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
+func Login(registry, username, password string) error {
|
||||
+ logLevelString := LogLevel.String()
|
||||
+ args := []string{"--log-level", logLevelString, "login", registry, "--password-stdin", "--username", username}
|
||||
+ stdin := bytes.NewBufferString(password)
|
||||
+
|
||||
+ if err := shell.Run("podman", stdin, nil, nil, args...); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
// Pull pulls an image
|
||||
func Pull(imageName string) error {
|
||||
var stderr bytes.Buffer
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From 49cd3c48c26d37c85aba5dbdcf04a995593ae673 Mon Sep 17 00:00:00 2001
|
||||
From: Debarshi Ray <rishi@fedoraproject.org>
|
||||
Date: Sun, 4 Jul 2021 18:11:04 +0200
|
||||
Subject: [PATCH 6/7] cmd/create: Split out the spinner around 'podman pull'
|
||||
|
||||
A subsequent commit will use this when retrying the 'podman pull'
|
||||
after logging the user into the registry for images that require
|
||||
authorization.
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/cmd/create.go | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/cmd/create.go b/src/cmd/create.go
|
||||
index e3245b4c5ae3..6a3005f06041 100644
|
||||
--- a/src/cmd/create.go
|
||||
+++ b/src/cmd/create.go
|
||||
@@ -731,6 +731,14 @@ func pullImage(image, release string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
+ if _, err := pullImageWithSpinner(imageFull); err != nil {
|
||||
+ return false, fmt.Errorf("failed to pull image %s", imageFull)
|
||||
+ }
|
||||
+
|
||||
+ return true, nil
|
||||
+}
|
||||
+
|
||||
+func pullImageWithSpinner(imageFull string) (bool, error) {
|
||||
logrus.Debugf("Pulling image %s", imageFull)
|
||||
|
||||
stdoutFd := os.Stdout.Fd()
|
||||
@@ -744,7 +752,7 @@ func pullImage(image, release string) (bool, error) {
|
||||
}
|
||||
|
||||
if err := podman.Pull(imageFull); err != nil {
|
||||
- return false, fmt.Errorf("failed to pull image %s", imageFull)
|
||||
+ return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
--
|
||||
2.31.1
|
||||
|
||||
|
||||
From 19bb0f08118536c32ff2d2f571d92758dff00dcb Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sun, 4 Jul 2021 18:11:28 +0200
|
||||
Subject: [PATCH 7/7] cmd/create: Support logging into a registry if necessary
|
||||
|
||||
Some registries contain private repositories of images and require the
|
||||
user to log in first to gain access. With this Toolbox tries to
|
||||
recognize errors when pulling images and offers the user the means to
|
||||
log in.
|
||||
|
||||
Some changes by Debarshi Ray.
|
||||
|
||||
https://github.com/containers/toolbox/pull/787
|
||||
---
|
||||
src/cmd/create.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 71 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/cmd/create.go b/src/cmd/create.go
|
||||
index 6a3005f06041..7cf03e8a4cd2 100644
|
||||
--- a/src/cmd/create.go
|
||||
+++ b/src/cmd/create.go
|
||||
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
+ "bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -668,6 +669,65 @@ func isPathReadWrite(path string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
+func logIntoRegistry(imageFull, registry string) (bool, error) {
|
||||
+ fmt.Printf("Image %s requires log-in.\n", imageFull)
|
||||
+
|
||||
+ scanner := bufio.NewScanner(os.Stdin)
|
||||
+
|
||||
+ stdinFd := os.Stdin.Fd()
|
||||
+ stdinFdInt := int(stdinFd)
|
||||
+
|
||||
+ if terminal.IsTerminal(stdinFdInt) {
|
||||
+ fmt.Printf("Username: ")
|
||||
+ }
|
||||
+
|
||||
+ if !scanner.Scan() {
|
||||
+ if err := scanner.Err(); err != nil {
|
||||
+ logrus.Debugf("Logging into registry: failed to read username: %s", err)
|
||||
+ }
|
||||
+
|
||||
+ return false, errors.New("failed to read username")
|
||||
+ }
|
||||
+
|
||||
+ username := scanner.Text()
|
||||
+
|
||||
+ var password string
|
||||
+
|
||||
+ if terminal.IsTerminal(stdinFdInt) {
|
||||
+ logrus.Debug("Reading password from a terminal input")
|
||||
+
|
||||
+ fmt.Printf("Password: ")
|
||||
+
|
||||
+ passwordBytes, err := terminal.ReadPassword(stdinFdInt)
|
||||
+ if err != nil {
|
||||
+ logrus.Debugf("Logging into registry: failed to read password: %s", err)
|
||||
+ return false, errors.New("failed to read password")
|
||||
+ }
|
||||
+
|
||||
+ password = string(passwordBytes)
|
||||
+ fmt.Println("")
|
||||
+ } else {
|
||||
+ logrus.Debug("Reading password from a non-terminal input")
|
||||
+
|
||||
+ if !scanner.Scan() {
|
||||
+ if err := scanner.Err(); err != nil {
|
||||
+ logrus.Debugf("Logging into registry: failed to read password: %s", err)
|
||||
+ }
|
||||
+
|
||||
+ return false, errors.New("failed to read password")
|
||||
+ }
|
||||
+
|
||||
+ password = scanner.Text()
|
||||
+ }
|
||||
+
|
||||
+ if err := podman.Login(registry, username, password); err != nil {
|
||||
+ logrus.Debugf("Logging into registry %s failed: %s", registry, err)
|
||||
+ return false, fmt.Errorf("failed to log into registry %s", registry)
|
||||
+ }
|
||||
+
|
||||
+ return true, nil
|
||||
+}
|
||||
+
|
||||
func pullImage(image, release string) (bool, error) {
|
||||
if _, err := utils.ImageReferenceCanBeID(image); err == nil {
|
||||
logrus.Debugf("Looking for image %s", image)
|
||||
@@ -732,7 +792,17 @@ func pullImage(image, release string) (bool, error) {
|
||||
}
|
||||
|
||||
if _, err := pullImageWithSpinner(imageFull); err != nil {
|
||||
- return false, fmt.Errorf("failed to pull image %s", imageFull)
|
||||
+ if !errors.Is(err, podman.ErrUnauthorized) {
|
||||
+ return false, fmt.Errorf("failed to pull image %s", imageFull)
|
||||
+ }
|
||||
+
|
||||
+ if _, err := logIntoRegistry(imageFull, domain); err != nil {
|
||||
+ return false, err
|
||||
+ }
|
||||
+
|
||||
+ if _, err := pullImageWithSpinner(imageFull); err != nil {
|
||||
+ return false, fmt.Errorf("failed to pull image %s", imageFull)
|
||||
+ }
|
||||
}
|
||||
|
||||
return true, nil
|
||||
--
|
||||
2.31.1
|
||||
|
@ -1,76 +0,0 @@
|
||||
From d03a5fee80f2f72da6a409f7ebc3b6caf21506e3 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Sat, 3 Jul 2021 20:39:49 +0200
|
||||
Subject: [PATCH] cmd/create: Expose the host's entire / in the container at
|
||||
/run/host
|
||||
|
||||
Having the entire host file system hierarchy mounted inside a toolbox
|
||||
container gives the containers a more complete environment that's
|
||||
resilient against future changes in the layout of the file system
|
||||
hierarchy and the need for giving access to new paths to support new
|
||||
use-cases. Otherwise, one would have to create a new container to get
|
||||
access to any path that lies outside the /boot, /etc, /run, /tmp, /usr
|
||||
and /var directories.
|
||||
|
||||
As a nice side-effect, this also simplifies the bind mount handling
|
||||
code.
|
||||
|
||||
https://github.com/containers/toolbox/pull/827
|
||||
---
|
||||
src/cmd/create.go | 29 +----------------------------
|
||||
1 file changed, 1 insertion(+), 28 deletions(-)
|
||||
|
||||
diff --git a/src/cmd/create.go b/src/cmd/create.go
|
||||
index ff533c26db98..297b336f6382 100644
|
||||
--- a/src/cmd/create.go
|
||||
+++ b/src/cmd/create.go
|
||||
@@ -265,28 +265,6 @@ func createContainer(container, image, release string, showCommandToEnter bool)
|
||||
logrus.Debugf("%s canonicalized to %s", currentUser.HomeDir, homeDirEvaled)
|
||||
homeDirMountArg := homeDirEvaled + ":" + homeDirEvaled + ":rslave"
|
||||
|
||||
- bootMountFlags := "ro"
|
||||
- isBootReadWrite, err := isPathReadWrite("/boot")
|
||||
- if err != nil {
|
||||
- return err
|
||||
- }
|
||||
- if isBootReadWrite {
|
||||
- bootMountFlags = "rw"
|
||||
- }
|
||||
-
|
||||
- bootMountArg := "/boot:/run/host/boot:" + bootMountFlags + ",rslave"
|
||||
-
|
||||
- usrMountFlags := "ro"
|
||||
- isUsrReadWrite, err := isPathReadWrite("/usr")
|
||||
- if err != nil {
|
||||
- return err
|
||||
- }
|
||||
- if isUsrReadWrite {
|
||||
- usrMountFlags = "rw"
|
||||
- }
|
||||
-
|
||||
- usrMountArg := "/usr:/run/host/usr:" + usrMountFlags + ",rslave"
|
||||
-
|
||||
var avahiSocketMount []string
|
||||
|
||||
avahiSocket, err := getServiceSocket("Avahi", "avahi-daemon.socket")
|
||||
@@ -423,16 +401,11 @@ func createContainer(container, image, release string, showCommandToEnter bool)
|
||||
createArgs = append(createArgs, []string{
|
||||
"--userns", usernsArg,
|
||||
"--user", "root:root",
|
||||
- "--volume", "/etc:/run/host/etc",
|
||||
+ "--volume", "/:/run/host:rslave",
|
||||
"--volume", "/dev:/dev:rslave",
|
||||
- "--volume", "/run:/run/host/run:rslave",
|
||||
- "--volume", "/tmp:/run/host/tmp:rslave",
|
||||
- "--volume", "/var:/run/host/var:rslave",
|
||||
- "--volume", bootMountArg,
|
||||
"--volume", dbusSystemSocketMountArg,
|
||||
"--volume", homeDirMountArg,
|
||||
"--volume", toolboxPathMountArg,
|
||||
- "--volume", usrMountArg,
|
||||
"--volume", runtimeDirectoryMountArg,
|
||||
}...)
|
||||
|
||||
--
|
||||
2.31.1
|
||||
|
@ -1,123 +0,0 @@
|
||||
From 6c86cabbe5da6e542b50c5c043b4d213c6279bbc Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= <harrymichal@seznam.cz>
|
||||
Date: Fri, 25 Jun 2021 16:04:52 +0200
|
||||
Subject: [PATCH] cmd/root: Make 'toolbox' create or fall back to a container
|
||||
if possible
|
||||
|
||||
This makes 'toolbox', without any commands specified, behave a lot like
|
||||
'toolbox enter'. When there aren't any toolbox containers, it will
|
||||
offer to create a new container matching the same parameters passed to
|
||||
the command. If there's just one toolbox container available, then it
|
||||
will fall back to it.
|
||||
|
||||
This makes the command line interface a lot similar to that of
|
||||
github.com/coreos/toolbox, which makes things easier for those
|
||||
switching over from it.
|
||||
|
||||
Some changes by Debarshi Ray.
|
||||
|
||||
https://github.com/containers/toolbox/pull/811
|
||||
---
|
||||
src/cmd/root.go | 65 +++++++++++++++++++++++++++++++++------
|
||||
test/system/002-help.bats | 8 -----
|
||||
2 files changed, 55 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/src/cmd/root.go b/src/cmd/root.go
|
||||
index d50135b9e963..7c4aef61eee8 100644
|
||||
--- a/src/cmd/root.go
|
||||
+++ b/src/cmd/root.go
|
||||
@@ -177,17 +177,62 @@ func rootHelp(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func rootRun(cmd *cobra.Command, args []string) error {
|
||||
- var builder strings.Builder
|
||||
- fmt.Fprintf(&builder, "missing command\n")
|
||||
- fmt.Fprintf(&builder, "\n")
|
||||
- fmt.Fprintf(&builder, "create Create a new toolbox container\n")
|
||||
- fmt.Fprintf(&builder, "enter Enter an existing toolbox container\n")
|
||||
- fmt.Fprintf(&builder, "list List all existing toolbox containers and images\n")
|
||||
- fmt.Fprintf(&builder, "\n")
|
||||
- fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
|
||||
+ if len(args) != 0 {
|
||||
+ panic("unexpected argument: commands known or unknown shouldn't reach here")
|
||||
+ }
|
||||
|
||||
- errMsg := builder.String()
|
||||
- return errors.New(errMsg)
|
||||
+ if utils.IsInsideContainer() {
|
||||
+ if !utils.IsInsideToolboxContainer() {
|
||||
+ return errors.New("this is not a toolbox container")
|
||||
+ }
|
||||
+
|
||||
+ if _, err := utils.ForwardToHost(); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+
|
||||
+ return nil
|
||||
+ }
|
||||
+
|
||||
+ container, image, release, err := utils.ResolveContainerAndImageNames("", "", "", "")
|
||||
+ if err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+
|
||||
+ userShell := os.Getenv("SHELL")
|
||||
+ if userShell == "" {
|
||||
+ return errors.New("failed to get the current user's default shell")
|
||||
+ }
|
||||
+
|
||||
+ command := []string{userShell, "-l"}
|
||||
+
|
||||
+ hostID, err := utils.GetHostID()
|
||||
+ if err != nil {
|
||||
+ return fmt.Errorf("failed to get the host ID: %w", err)
|
||||
+ }
|
||||
+
|
||||
+ hostVariantID, err := utils.GetHostVariantID()
|
||||
+ if err != nil {
|
||||
+ return errors.New("failed to get the host VARIANT_ID")
|
||||
+ }
|
||||
+
|
||||
+ var emitEscapeSequence bool
|
||||
+
|
||||
+ if hostID == "fedora" && (hostVariantID == "silverblue" || hostVariantID == "workstation") {
|
||||
+ emitEscapeSequence = true
|
||||
+ }
|
||||
+
|
||||
+ if err := runCommand(container,
|
||||
+ true,
|
||||
+ image,
|
||||
+ release,
|
||||
+ command,
|
||||
+ emitEscapeSequence,
|
||||
+ true,
|
||||
+ false); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+
|
||||
+ return nil
|
||||
}
|
||||
|
||||
func rootUsage(cmd *cobra.Command) error {
|
||||
diff --git a/test/system/002-help.bats b/test/system/002-help.bats
|
||||
index 8a057ddb3818..4ff02c6215e7 100644
|
||||
--- a/test/system/002-help.bats
|
||||
+++ b/test/system/002-help.bats
|
||||
@@ -4,14 +4,6 @@ load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/helpers.bash'
|
||||
|
||||
-@test "help: Try to run toolbox with no command (shows usage screen)" {
|
||||
- run $TOOLBOX
|
||||
-
|
||||
- assert_failure
|
||||
- assert_line --index 0 "Error: missing command"
|
||||
- assert_output --partial "Run 'toolbox --help' for usage."
|
||||
-}
|
||||
-
|
||||
@test "help: Run command 'help'" {
|
||||
run $TOOLBOX help
|
||||
|
||||
--
|
||||
2.31.1
|
||||
|
20
toolbox.spec
20
toolbox.spec
@ -4,12 +4,12 @@
|
||||
%global _find_debuginfo_dwz_opts %{nil}
|
||||
|
||||
Name: toolbox
|
||||
Version: 0.0.99.2
|
||||
Version: 0.0.99.2^1.git660b6970e998
|
||||
|
||||
%global goipath github.com/containers/%{name}
|
||||
%gometa
|
||||
|
||||
Release: 3%{?dist}
|
||||
Release: 1%{?dist}
|
||||
Summary: Unprivileged development environment
|
||||
|
||||
License: ASL 2.0
|
||||
@ -21,15 +21,9 @@ URL: https://github.com/containers/%{name}
|
||||
# $ go mod vendor
|
||||
Source0: %{name}-%{version}-vendored.tar.xz
|
||||
|
||||
# Upstream
|
||||
Patch0: toolbox-cmd-root-Make-toolbox-create-or-fall-back-to-a-conta.patch
|
||||
Patch1: toolbox-cmd-create-Expose-the-host-s-entire-in-the-container.patch
|
||||
|
||||
# RHEL specific
|
||||
Patch100: toolbox-Make-the-build-flags-match-RHEL-s-gobuild.patch
|
||||
Patch101: toolbox-Make-the-build-flags-match-RHEL-s-gobuild-for-PPC64.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1940054
|
||||
Patch102: toolbox-Support-logging-into-a-registry-if-necessary.patch
|
||||
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1905383
|
||||
ExcludeArch: %{ix86}
|
||||
@ -61,17 +55,12 @@ The %{name}-tests package contains system tests for %{name}.
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%patch0 -p1
|
||||
%patch1 -p1
|
||||
|
||||
%ifnarch ppc64
|
||||
%patch100 -p1
|
||||
%else
|
||||
%patch101 -p1
|
||||
%endif
|
||||
|
||||
%patch102 -p1
|
||||
|
||||
# %%gomkdir is absent from RHEL 8.
|
||||
GOBUILDDIR="$(pwd)/_build"
|
||||
GOSOURCEDIR="$(pwd)"
|
||||
@ -108,6 +97,7 @@ ln -s src/vendor vendor
|
||||
%{_datadir}/bash-completion
|
||||
%{_mandir}/man1/%{name}.1*
|
||||
%{_mandir}/man1/%{name}-*.1*
|
||||
%config(noreplace) %{_sysconfdir}/containers/%{name}.conf
|
||||
%{_sysconfdir}/profile.d/%{name}.sh
|
||||
%{_tmpfilesdir}/%{name}.conf
|
||||
|
||||
@ -116,6 +106,10 @@ ln -s src/vendor vendor
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Jul 28 2021 Jindrich Novy <jnovy@redhat.com> - 0.0.99.2^1.git660b6970e998-1
|
||||
- Add support for configuration files
|
||||
- Related: #1970747
|
||||
|
||||
* Sat Jul 10 2021 Jindrich Novy <jnovy@redhat.com> - 0.0.99.2-3
|
||||
- Expose the host's entire / in the container at /run/host
|
||||
- Resolves: #1977343
|
||||
|
Loading…
Reference in New Issue
Block a user