github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/image/cache/compare_test.go (about)

     1  package cache // import "github.com/docker/docker/image/cache"
     2  
     3  import (
     4  	"runtime"
     5  	"testing"
     6  
     7  	"github.com/docker/docker/api/types/container"
     8  	"github.com/docker/docker/api/types/strslice"
     9  	"github.com/docker/go-connections/nat"
    10  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    11  	"gotest.tools/v3/assert"
    12  	is "gotest.tools/v3/assert/cmp"
    13  )
    14  
    15  // Just to make life easier
    16  func newPortNoError(proto, port string) nat.Port {
    17  	p, _ := nat.NewPort(proto, port)
    18  	return p
    19  }
    20  
    21  func TestCompare(t *testing.T) {
    22  	ports1 := make(nat.PortSet)
    23  	ports1[newPortNoError("tcp", "1111")] = struct{}{}
    24  	ports1[newPortNoError("tcp", "2222")] = struct{}{}
    25  	ports2 := make(nat.PortSet)
    26  	ports2[newPortNoError("tcp", "3333")] = struct{}{}
    27  	ports2[newPortNoError("tcp", "4444")] = struct{}{}
    28  	ports3 := make(nat.PortSet)
    29  	ports3[newPortNoError("tcp", "1111")] = struct{}{}
    30  	ports3[newPortNoError("tcp", "2222")] = struct{}{}
    31  	ports3[newPortNoError("tcp", "5555")] = struct{}{}
    32  	volumes1 := make(map[string]struct{})
    33  	volumes1["/test1"] = struct{}{}
    34  	volumes2 := make(map[string]struct{})
    35  	volumes2["/test2"] = struct{}{}
    36  	volumes3 := make(map[string]struct{})
    37  	volumes3["/test1"] = struct{}{}
    38  	volumes3["/test3"] = struct{}{}
    39  	envs1 := []string{"ENV1=value1", "ENV2=value2"}
    40  	envs2 := []string{"ENV1=value1", "ENV3=value3"}
    41  	entrypoint1 := strslice.StrSlice{"/bin/sh", "-c"}
    42  	entrypoint2 := strslice.StrSlice{"/bin/sh", "-d"}
    43  	entrypoint3 := strslice.StrSlice{"/bin/sh", "-c", "echo"}
    44  	cmd1 := strslice.StrSlice{"/bin/sh", "-c"}
    45  	cmd2 := strslice.StrSlice{"/bin/sh", "-d"}
    46  	cmd3 := strslice.StrSlice{"/bin/sh", "-c", "echo"}
    47  	labels1 := map[string]string{"LABEL1": "value1", "LABEL2": "value2"}
    48  	labels2 := map[string]string{"LABEL1": "value1", "LABEL2": "value3"}
    49  	labels3 := map[string]string{"LABEL1": "value1", "LABEL2": "value2", "LABEL3": "value3"}
    50  
    51  	sameConfigs := map[*container.Config]*container.Config{
    52  		// Empty config
    53  		{}: {},
    54  		// Does not compare hostname, domainname & image
    55  		{
    56  			Hostname:   "host1",
    57  			Domainname: "domain1",
    58  			Image:      "image1",
    59  			User:       "user",
    60  		}: {
    61  			Hostname:   "host2",
    62  			Domainname: "domain2",
    63  			Image:      "image2",
    64  			User:       "user",
    65  		},
    66  		// only OpenStdin
    67  		{OpenStdin: false}: {OpenStdin: false},
    68  		// only env
    69  		{Env: envs1}: {Env: envs1},
    70  		// only cmd
    71  		{Cmd: cmd1}: {Cmd: cmd1},
    72  		// only labels
    73  		{Labels: labels1}: {Labels: labels1},
    74  		// only exposedPorts
    75  		{ExposedPorts: ports1}: {ExposedPorts: ports1},
    76  		// only entrypoints
    77  		{Entrypoint: entrypoint1}: {Entrypoint: entrypoint1},
    78  		// only volumes
    79  		{Volumes: volumes1}: {Volumes: volumes1},
    80  	}
    81  	differentConfigs := map[*container.Config]*container.Config{
    82  		nil: nil,
    83  		{
    84  			Hostname:   "host1",
    85  			Domainname: "domain1",
    86  			Image:      "image1",
    87  			User:       "user1",
    88  		}: {
    89  			Hostname:   "host1",
    90  			Domainname: "domain1",
    91  			Image:      "image1",
    92  			User:       "user2",
    93  		},
    94  		// only OpenStdin
    95  		{OpenStdin: false}: {OpenStdin: true},
    96  		{OpenStdin: true}:  {OpenStdin: false},
    97  		// only env
    98  		{Env: envs1}: {Env: envs2},
    99  		// only cmd
   100  		{Cmd: cmd1}: {Cmd: cmd2},
   101  		// not the same number of parts
   102  		{Cmd: cmd1}: {Cmd: cmd3},
   103  		// only labels
   104  		{Labels: labels1}: {Labels: labels2},
   105  		// not the same number of labels
   106  		{Labels: labels1}: {Labels: labels3},
   107  		// only exposedPorts
   108  		{ExposedPorts: ports1}: {ExposedPorts: ports2},
   109  		// not the same number of ports
   110  		{ExposedPorts: ports1}: {ExposedPorts: ports3},
   111  		// only entrypoints
   112  		{Entrypoint: entrypoint1}: {Entrypoint: entrypoint2},
   113  		// not the same number of parts
   114  		{Entrypoint: entrypoint1}: {Entrypoint: entrypoint3},
   115  		// only volumes
   116  		{Volumes: volumes1}: {Volumes: volumes2},
   117  		// not the same number of labels
   118  		{Volumes: volumes1}: {Volumes: volumes3},
   119  	}
   120  	for config1, config2 := range sameConfigs {
   121  		if !compare(config1, config2) {
   122  			t.Fatalf("Compare should be true for [%v] and [%v]", config1, config2)
   123  		}
   124  	}
   125  	for config1, config2 := range differentConfigs {
   126  		if compare(config1, config2) {
   127  			t.Fatalf("Compare should be false for [%v] and [%v]", config1, config2)
   128  		}
   129  	}
   130  }
   131  
   132  func TestPlatformCompare(t *testing.T) {
   133  	for _, tc := range []struct {
   134  		name     string
   135  		builder  ocispec.Platform
   136  		image    ocispec.Platform
   137  		expected bool
   138  	}{
   139  		{
   140  			name:     "same os and arch",
   141  			builder:  ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
   142  			image:    ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
   143  			expected: true,
   144  		},
   145  		{
   146  			name:     "same os different arch",
   147  			builder:  ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS},
   148  			image:    ocispec.Platform{Architecture: "arm64", OS: runtime.GOOS},
   149  			expected: false,
   150  		},
   151  		{
   152  			name:     "same os smaller host variant",
   153  			builder:  ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS},
   154  			image:    ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS},
   155  			expected: false,
   156  		},
   157  		{
   158  			name:     "same os higher host variant",
   159  			builder:  ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS},
   160  			image:    ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS},
   161  			expected: true,
   162  		},
   163  		{
   164  			// Test for https://github.com/moby/moby/issues/47307
   165  			name:     "different build and revision",
   166  			builder:  ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.22621"},
   167  			image:    ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
   168  			expected: true,
   169  		},
   170  		{
   171  			name:     "different revision",
   172  			builder:  ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.1234"},
   173  			image:    ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
   174  			expected: true,
   175  		},
   176  		{
   177  			name:     "different major",
   178  			builder:  ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "11.0.17763.5329"},
   179  			image:    ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
   180  			expected: false,
   181  		},
   182  		{
   183  			name:     "different minor same osver",
   184  			builder:  ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
   185  			image:    ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.1.17763.5329"},
   186  			expected: false,
   187  		},
   188  		{
   189  			name:     "different arch same osver",
   190  			builder:  ocispec.Platform{Architecture: "arm64", OS: "windows", OSVersion: "10.0.17763.5329"},
   191  			image:    ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"},
   192  			expected: false,
   193  		},
   194  	} {
   195  		tc := tc
   196  		// OSVersion comparison is only performed by containerd platform
   197  		// matcher if built on Windows.
   198  		if (tc.image.OSVersion != "" || tc.builder.OSVersion != "") && runtime.GOOS != "windows" {
   199  			continue
   200  		}
   201  
   202  		t.Run(tc.name, func(t *testing.T) {
   203  			assert.Check(t, is.Equal(comparePlatform(tc.builder, tc.image), tc.expected))
   204  		})
   205  	}
   206  }