sigs.k8s.io/cluster-api@v1.7.1/util/container/image_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package container
    18  
    19  import (
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/distribution/reference"
    24  	. "github.com/onsi/gomega"
    25  )
    26  
    27  func TestParseImageName(t *testing.T) {
    28  	testCases := []struct {
    29  		name      string
    30  		input     string
    31  		repo      string
    32  		imageName string
    33  		tag       string
    34  		digest    string
    35  		wantError bool
    36  	}{
    37  		{
    38  			name:      "input with path and tag",
    39  			input:     "registry.k8s.io/dev/coredns:1.6.2",
    40  			repo:      "registry.k8s.io/dev",
    41  			imageName: "coredns",
    42  			tag:       "1.6.2",
    43  			wantError: false,
    44  		},
    45  		{
    46  			name:      "input with name only",
    47  			input:     "example.com/root",
    48  			repo:      "example.com",
    49  			imageName: "root",
    50  			wantError: false,
    51  		},
    52  		{
    53  			name:      "input with name and tag without path",
    54  			input:     "example.com/root:tag",
    55  			repo:      "example.com",
    56  			imageName: "root",
    57  			tag:       "tag",
    58  			wantError: false,
    59  		},
    60  		{
    61  			name:      "input with name and digest without tag",
    62  			input:     "example.com/root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    63  			repo:      "example.com",
    64  			imageName: "root",
    65  			digest:    "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    66  			wantError: false,
    67  		},
    68  		{
    69  			name:      "input with path and name",
    70  			input:     "example.com/user/repo",
    71  			repo:      "example.com/user",
    72  			imageName: "repo",
    73  			wantError: false,
    74  		},
    75  		{
    76  			name:      "input with path, name and tag",
    77  			input:     "example.com/user/repo:tag",
    78  			repo:      "example.com/user",
    79  			imageName: "repo",
    80  			tag:       "tag",
    81  			wantError: false,
    82  		},
    83  		{
    84  			name:      "input with path, name, tag and digest",
    85  			input:     "example.com/user/repo:tag@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    86  			repo:      "example.com/user",
    87  			imageName: "repo",
    88  			tag:       "tag",
    89  			digest:    "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    90  			wantError: false,
    91  		},
    92  		{
    93  			name:      "input with url with port",
    94  			input:     "url:5000/repo",
    95  			repo:      "url:5000",
    96  			imageName: "repo",
    97  			wantError: false,
    98  		},
    99  		{
   100  			name:      "input with url with port and tag",
   101  			input:     "url:5000/repo:tag",
   102  			repo:      "url:5000",
   103  			imageName: "repo",
   104  			tag:       "tag",
   105  			wantError: false,
   106  		},
   107  		{
   108  			name:      "input with url with port and digest",
   109  			input:     "url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
   110  			repo:      "url:5000",
   111  			imageName: "repo",
   112  			digest:    "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
   113  			wantError: false,
   114  		},
   115  		{
   116  			name:      "input with invalid image name",
   117  			input:     "url$#",
   118  			repo:      "",
   119  			imageName: "",
   120  			tag:       "",
   121  			wantError: true,
   122  		},
   123  		{
   124  			name:      "input with a docker.io/ image name",
   125  			input:     "docker.io/dev/kube-proxy:v1.99.99",
   126  			repo:      "docker.io/dev",
   127  			imageName: "kube-proxy",
   128  			tag:       "v1.99.99",
   129  			wantError: false,
   130  		},
   131  		{
   132  			name:      "input with a docker.io/ image name that is not canonical",
   133  			input:     "dev/kube-proxy:v1.99.99",
   134  			repo:      "dev",
   135  			imageName: "kube-proxy",
   136  			tag:       "v1.99.99",
   137  			wantError: true,
   138  		},
   139  	}
   140  	for _, tc := range testCases {
   141  		g := NewWithT(t)
   142  
   143  		t.Run(tc.name, func(*testing.T) {
   144  			image, err := ImageFromString(tc.input)
   145  			if tc.wantError {
   146  				g.Expect(err).To(HaveOccurred())
   147  			} else {
   148  				g.Expect(err).ToNot(HaveOccurred())
   149  				g.Expect(image.Repository).To(Equal(tc.repo))
   150  				g.Expect(image.Name).To(Equal(tc.imageName))
   151  				g.Expect(image.Tag).To(Equal(tc.tag))
   152  				g.Expect(image.Digest).To(Equal(tc.digest))
   153  				g.Expect(image.String()).To(Equal(tc.input))
   154  			}
   155  		})
   156  	}
   157  }
   158  
   159  func TestModifyImageRepository(t *testing.T) {
   160  	const testRepository = "example.com/new"
   161  
   162  	testCases := []struct {
   163  		name           string
   164  		image          string
   165  		repo           string
   166  		want           string
   167  		wantError      bool
   168  		wantErrMessage string
   169  	}{
   170  		{
   171  			name:           "updates the repository of the image",
   172  			image:          "example.com/subpaths/are/okay/image:1.17.3",
   173  			repo:           testRepository,
   174  			want:           "example.com/new/image:1.17.3",
   175  			wantError:      false,
   176  			wantErrMessage: "",
   177  		},
   178  		{
   179  			name:           "errors if the repository name is too long",
   180  			image:          "example.com/image:1.17.3",
   181  			repo:           strings.Repeat("a", 255),
   182  			want:           "",
   183  			wantError:      true,
   184  			wantErrMessage: reference.ErrNameTooLong.Error(),
   185  		},
   186  		{
   187  			name:           "errors if the image name is not canonical",
   188  			image:          "image:1.17.3",
   189  			repo:           testRepository,
   190  			want:           "",
   191  			wantError:      true,
   192  			wantErrMessage: reference.ErrNameNotCanonical.Error(),
   193  		},
   194  		{
   195  			name:           "errors if the image name is not tagged",
   196  			image:          "example.com/image",
   197  			repo:           testRepository,
   198  			want:           "",
   199  			wantError:      true,
   200  			wantErrMessage: "image must be tagged",
   201  		},
   202  		{
   203  			name:           "errors if the image name is not valid",
   204  			image:          "example.com/image:$@$(*",
   205  			repo:           testRepository,
   206  			want:           "",
   207  			wantError:      true,
   208  			wantErrMessage: "failed to parse image name",
   209  		},
   210  	}
   211  	for _, tc := range testCases {
   212  		g := NewWithT(t)
   213  
   214  		t.Run(tc.name, func(*testing.T) {
   215  			res, err := ModifyImageRepository(tc.image, tc.repo)
   216  			if tc.wantError {
   217  				g.Expect(err).To(HaveOccurred())
   218  				g.Expect(err).To(MatchError(ContainSubstring(tc.wantErrMessage)))
   219  			} else {
   220  				g.Expect(err).ToNot(HaveOccurred())
   221  				g.Expect(res).To(Equal(tc.want))
   222  			}
   223  		})
   224  	}
   225  }
   226  
   227  func TestModifyImageTag(t *testing.T) {
   228  	g := NewWithT(t)
   229  	t.Run("should ensure image is a docker compatible tag", func(*testing.T) {
   230  		testTag := "v1.17.4+build1"
   231  		image := "example.com/image:1.17.3"
   232  		res, err := ModifyImageTag(image, testTag)
   233  		g.Expect(err).ToNot(HaveOccurred())
   234  		g.Expect(res).To(Equal("example.com/image:v1.17.4_build1"))
   235  	})
   236  	t.Run("should ensure image is a docker compatible tag with docker.io", func(*testing.T) {
   237  		testTag := "v1.17.4+build1"
   238  		image := "docker.io/dev/image:1.17.3"
   239  		res, err := ModifyImageTag(image, testTag)
   240  		g.Expect(err).ToNot(HaveOccurred())
   241  		g.Expect(res).To(Equal("docker.io/dev/image:v1.17.4_build1"))
   242  	})
   243  }