mirror of https://github.com/knative/func.git
fix: make image digest check more permissive (#2510)
* fix: make image digest check more permissive * use extant implementation for digest check
This commit is contained in:
parent
63e3e52294
commit
eb17ea77e8
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/ory/viper"
|
"github.com/ory/viper"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
@ -785,51 +786,13 @@ func printDeployMessages(out io.Writer, f fn.Function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isDigested returns true if provided image string 'v' has digest and false if not.
|
// isDigested checks that the given image reference has a digest. Invalid
|
||||||
// Includes basic validation that a provided digest is correctly formatted.
|
// reference return error.
|
||||||
// Given that image is not digested, image will still be validated and return
|
|
||||||
// a combination of bool (img has valid digest) and err (img is in valid format)
|
|
||||||
// Therefore returned combination of [false,nil] means "valid undigested image".
|
|
||||||
func isDigested(v string) (validDigest bool, err error) {
|
func isDigested(v string) (validDigest bool, err error) {
|
||||||
var digest string
|
ref, err := name.ParseReference(v)
|
||||||
vv := strings.Split(v, "@")
|
if err != nil {
|
||||||
if len(vv) < 2 {
|
return false, err
|
||||||
// image does NOT have a digest, validate further
|
|
||||||
if v == "" {
|
|
||||||
err = fmt.Errorf("provided image is empty, cannot validate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vvv := strings.Split(v, ":")
|
|
||||||
if len(vvv) < 2 {
|
|
||||||
// assume user knows what hes doing
|
|
||||||
return
|
|
||||||
} else if len(vvv) > 2 {
|
|
||||||
err = fmt.Errorf("image '%v' contains an invalid tag (extra ':')", v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tag := vvv[1]
|
|
||||||
if tag == "" {
|
|
||||||
err = fmt.Errorf("image '%v' has an empty tag", v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if len(vv) > 2 {
|
|
||||||
// image is invalid
|
|
||||||
err = fmt.Errorf("image '%v' contains an invalid digest (extra '@')", v)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// image has a digest, validate further
|
_, ok := ref.(name.Digest)
|
||||||
digest = vv[1]
|
return ok, nil
|
||||||
|
|
||||||
if !strings.HasPrefix(digest, "sha256:") {
|
|
||||||
err = fmt.Errorf("image digest '%s' requires 'sha256:' prefix", digest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(digest[7:]) != 64 {
|
|
||||||
err = fmt.Errorf("image digest '%v' has an invalid sha256 hash length of %v when it should be 64", digest, len(digest[7:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
validDigest = true
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -756,34 +756,34 @@ func TestDeploy_ImageWithDigestErrors(t *testing.T) {
|
||||||
image string // value to provide as --image
|
image string // value to provide as --image
|
||||||
build string // If provided, the value of the build flag
|
build string // If provided, the value of the build flag
|
||||||
push bool // if true, explicitly set argument --push=true
|
push bool // if true, explicitly set argument --push=true
|
||||||
errString string // the string value of an expected error
|
errPrefix string // the string value of an expected error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "correctly formatted full image with digest yields no error (degen case)",
|
name: "correctly formatted full image with digest yields no error (degen case)",
|
||||||
image: "example.com/myNamespace/myFunction:latest@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
image: "example.com/namespace/myfunction@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
||||||
build: "false",
|
build: "false",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "--build forced on yields error",
|
name: "--build forced on yields error",
|
||||||
image: "example.com/myNamespace/myFunction:latest@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
image: "example.com/mynamespace/myfunction@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
||||||
build: "true",
|
build: "true",
|
||||||
errString: "building can not be enabled when using an image with digest",
|
errPrefix: "building can not be enabled when using an image with digest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "push flag explicitly set with digest should error",
|
name: "push flag explicitly set with digest should error",
|
||||||
image: "example.com/myNamespace/myFunction:latest@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
image: "example.com/mynamespace/myfunction@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
||||||
push: true,
|
push: true,
|
||||||
errString: "pushing is not valid when specifying an image with digest",
|
errPrefix: "pushing is not valid when specifying an image with digest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid digest prefix 'Xsha256', expect error",
|
name: "invalid digest prefix 'Xsha256', expect error",
|
||||||
image: "example.com/myNamespace/myFunction:latest@Xsha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
image: "example.com/mynamespace/myfunction@Xsha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e",
|
||||||
errString: "image digest 'Xsha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4e' requires 'sha256:' prefix",
|
errPrefix: "could not parse reference",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid sha hash length(added X at the end), expect error",
|
name: "invalid sha hash length(added X at the end), expect error",
|
||||||
image: "example.com/myNamespace/myFunction:latest@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4eX",
|
image: "example.com/mynamespace/myfunction@sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4eX",
|
||||||
errString: "image digest 'sha256:7d66645b0add6de7af77ef332ecd4728649a2f03b9a2716422a054805b595c4eX' has an invalid sha256 hash length of 65 when it should be 64",
|
errPrefix: "could not parse reference",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,11 +823,11 @@ func TestDeploy_ImageWithDigestErrors(t *testing.T) {
|
||||||
cmd.SetArgs(args)
|
cmd.SetArgs(args)
|
||||||
err = cmd.Execute()
|
err = cmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if tt.errString == "" {
|
if tt.errPrefix == "" {
|
||||||
t.Fatal(err) // no error was expected. fail
|
t.Fatal(err) // no error was expected. fail
|
||||||
}
|
}
|
||||||
if tt.errString != err.Error() {
|
if !strings.HasPrefix(err.Error(), tt.errPrefix) {
|
||||||
t.Fatalf("expected error '%v' not received. got '%v'", tt.errString, err.Error())
|
t.Fatalf("expected error prefix '%v' not received. got '%v'", tt.errPrefix, err.Error())
|
||||||
}
|
}
|
||||||
// There was an error, but it was expected
|
// There was an error, but it was expected
|
||||||
}
|
}
|
||||||
|
@ -842,7 +842,7 @@ func TestDeploy_ImageWithDigestErrors(t *testing.T) {
|
||||||
func TestDeploy_ImageWithDigestDoesntPopulateBuild(t *testing.T) {
|
func TestDeploy_ImageWithDigestDoesntPopulateBuild(t *testing.T) {
|
||||||
root := FromTempDirectory(t)
|
root := FromTempDirectory(t)
|
||||||
// image with digest (well almost, atleast in length and syntax)
|
// image with digest (well almost, atleast in length and syntax)
|
||||||
const img = "docker.io/4141gauron3268@sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
const img = "docker.io/4141gauron3268@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||||
// Create a new Function in the temp directory
|
// Create a new Function in the temp directory
|
||||||
_, err := fn.New().Init(fn.Function{Runtime: "go", Root: root})
|
_, err := fn.New().Init(fn.Function{Runtime: "go", Root: root})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -869,7 +869,7 @@ func TestDeploy_ImageWithDigestDoesntPopulateBuild(t *testing.T) {
|
||||||
// TestDeploy_WithoutDigest ensures that images specified with --image and
|
// TestDeploy_WithoutDigest ensures that images specified with --image and
|
||||||
// without digest are correctly processed and propagated to .Deploy.Image
|
// without digest are correctly processed and propagated to .Deploy.Image
|
||||||
func TestDeploy_WithoutDigest(t *testing.T) {
|
func TestDeploy_WithoutDigest(t *testing.T) {
|
||||||
const sha = "sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
const sha = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string //name of the test
|
name string //name of the test
|
||||||
|
@ -2140,3 +2140,34 @@ func TestDeploy_CorrectImageDeployed(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test_isDigested ensures that the function is properly delegating to
|
||||||
|
// by checking both that it will fail on an invalid reference and will
|
||||||
|
// return true if the image contains a digest and false otherwise.
|
||||||
|
// See the delegate from the implementation for comprehensive tests.
|
||||||
|
func Test_isDigested(t *testing.T) {
|
||||||
|
var digested bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Ensure validation
|
||||||
|
_, err = isDigested("invalid&image@sha256:12345")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("did not validate image reference")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure positive case
|
||||||
|
if digested, err = isDigested("alpine"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if digested {
|
||||||
|
t.Fatal("reported digested on undigested image reference")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure negative case
|
||||||
|
if digested, err = isDigested("alpine@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !digested {
|
||||||
|
t.Fatal("did not report image reference has digest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue