github.com/rish1988/moby@v25.0.2+incompatible/client/image_push_test.go (about)

     1  package client // import "github.com/docker/docker/client"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/docker/docker/api/types/image"
    13  	"github.com/docker/docker/api/types/registry"
    14  	"github.com/docker/docker/errdefs"
    15  	"gotest.tools/v3/assert"
    16  	is "gotest.tools/v3/assert/cmp"
    17  )
    18  
    19  func TestImagePushReferenceError(t *testing.T) {
    20  	client := &Client{
    21  		client: newMockClient(func(req *http.Request) (*http.Response, error) {
    22  			return nil, nil
    23  		}),
    24  	}
    25  	// An empty reference is an invalid reference
    26  	_, err := client.ImagePush(context.Background(), "", image.PushOptions{})
    27  	if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
    28  		t.Fatalf("expected an error, got %v", err)
    29  	}
    30  	// An canonical reference cannot be pushed
    31  	_, err = client.ImagePush(context.Background(), "repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", image.PushOptions{})
    32  	if err == nil || err.Error() != "cannot push a digest reference" {
    33  		t.Fatalf("expected an error, got %v", err)
    34  	}
    35  }
    36  
    37  func TestImagePushAnyError(t *testing.T) {
    38  	client := &Client{
    39  		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
    40  	}
    41  	_, err := client.ImagePush(context.Background(), "myimage", image.PushOptions{})
    42  	assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
    43  }
    44  
    45  func TestImagePushStatusUnauthorizedError(t *testing.T) {
    46  	client := &Client{
    47  		client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
    48  	}
    49  	_, err := client.ImagePush(context.Background(), "myimage", image.PushOptions{})
    50  	assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
    51  }
    52  
    53  func TestImagePushWithUnauthorizedErrorAndPrivilegeFuncError(t *testing.T) {
    54  	client := &Client{
    55  		client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
    56  	}
    57  	privilegeFunc := func() (string, error) {
    58  		return "", fmt.Errorf("Error requesting privilege")
    59  	}
    60  	_, err := client.ImagePush(context.Background(), "myimage", image.PushOptions{
    61  		PrivilegeFunc: privilegeFunc,
    62  	})
    63  	if err == nil || err.Error() != "Error requesting privilege" {
    64  		t.Fatalf("expected an error requesting privilege, got %v", err)
    65  	}
    66  }
    67  
    68  func TestImagePushWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing.T) {
    69  	client := &Client{
    70  		client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")),
    71  	}
    72  	privilegeFunc := func() (string, error) {
    73  		return "a-auth-header", nil
    74  	}
    75  	_, err := client.ImagePush(context.Background(), "myimage", image.PushOptions{
    76  		PrivilegeFunc: privilegeFunc,
    77  	})
    78  	assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
    79  }
    80  
    81  func TestImagePushWithPrivilegedFuncNoError(t *testing.T) {
    82  	expectedURL := "/images/myimage/push"
    83  	client := &Client{
    84  		client: newMockClient(func(req *http.Request) (*http.Response, error) {
    85  			if !strings.HasPrefix(req.URL.Path, expectedURL) {
    86  				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
    87  			}
    88  			auth := req.Header.Get(registry.AuthHeader)
    89  			if auth == "NotValid" {
    90  				return &http.Response{
    91  					StatusCode: http.StatusUnauthorized,
    92  					Body:       io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))),
    93  				}, nil
    94  			}
    95  			if auth != "IAmValid" {
    96  				return nil, fmt.Errorf("invalid auth header: expected %s, got %s", "IAmValid", auth)
    97  			}
    98  			query := req.URL.Query()
    99  			tag := query.Get("tag")
   100  			if tag != "tag" {
   101  				return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", "tag", tag)
   102  			}
   103  			return &http.Response{
   104  				StatusCode: http.StatusOK,
   105  				Body:       io.NopCloser(bytes.NewReader([]byte("hello world"))),
   106  			}, nil
   107  		}),
   108  	}
   109  	privilegeFunc := func() (string, error) {
   110  		return "IAmValid", nil
   111  	}
   112  	resp, err := client.ImagePush(context.Background(), "myimage:tag", image.PushOptions{
   113  		RegistryAuth:  "NotValid",
   114  		PrivilegeFunc: privilegeFunc,
   115  	})
   116  	if err != nil {
   117  		t.Fatal(err)
   118  	}
   119  	body, err := io.ReadAll(resp)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	if string(body) != "hello world" {
   124  		t.Fatalf("expected 'hello world', got %s", string(body))
   125  	}
   126  }
   127  
   128  func TestImagePushWithoutErrors(t *testing.T) {
   129  	expectedOutput := "hello world"
   130  	expectedURLFormat := "/images/%s/push"
   131  	testCases := []struct {
   132  		all           bool
   133  		reference     string
   134  		expectedImage string
   135  		expectedTag   string
   136  	}{
   137  		{
   138  			all:           false,
   139  			reference:     "myimage",
   140  			expectedImage: "myimage",
   141  			expectedTag:   "latest",
   142  		},
   143  		{
   144  			all:           false,
   145  			reference:     "myimage:tag",
   146  			expectedImage: "myimage",
   147  			expectedTag:   "tag",
   148  		},
   149  		{
   150  			all:           true,
   151  			reference:     "myimage",
   152  			expectedImage: "myimage",
   153  			expectedTag:   "",
   154  		},
   155  		{
   156  			all:           true,
   157  			reference:     "myimage:anything",
   158  			expectedImage: "myimage",
   159  			expectedTag:   "",
   160  		},
   161  	}
   162  	for _, tc := range testCases {
   163  		tc := tc
   164  		t.Run(fmt.Sprintf("%s,all-tags=%t", tc.reference, tc.all), func(t *testing.T) {
   165  			client := &Client{
   166  				client: newMockClient(func(req *http.Request) (*http.Response, error) {
   167  					expectedURL := fmt.Sprintf(expectedURLFormat, tc.expectedImage)
   168  					if !strings.HasPrefix(req.URL.Path, expectedURL) {
   169  						return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
   170  					}
   171  					query := req.URL.Query()
   172  					tag := query.Get("tag")
   173  					if tag != tc.expectedTag {
   174  						return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", tc.expectedTag, tag)
   175  					}
   176  					return &http.Response{
   177  						StatusCode: http.StatusOK,
   178  						Body:       io.NopCloser(bytes.NewReader([]byte(expectedOutput))),
   179  					}, nil
   180  				}),
   181  			}
   182  			resp, err := client.ImagePush(context.Background(), tc.reference, image.PushOptions{
   183  				All: tc.all,
   184  			})
   185  			if err != nil {
   186  				t.Fatal(err)
   187  			}
   188  			body, err := io.ReadAll(resp)
   189  			if err != nil {
   190  				t.Fatal(err)
   191  			}
   192  			if string(body) != expectedOutput {
   193  				t.Fatalf("expected '%s', got %s", expectedOutput, string(body))
   194  			}
   195  		})
   196  	}
   197  }