zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/cli/client/server_info_cmd_test.go (about)

     1  //go:build search
     2  // +build search
     3  
     4  package client //nolint:testpackage
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"regexp"
    12  	"strings"
    13  	"testing"
    14  
    15  	. "github.com/smartystreets/goconvey/convey"
    16  
    17  	zerr "zotregistry.dev/zot/errors"
    18  	"zotregistry.dev/zot/pkg/api"
    19  	"zotregistry.dev/zot/pkg/api/config"
    20  	"zotregistry.dev/zot/pkg/api/constants"
    21  	extconf "zotregistry.dev/zot/pkg/extensions/config"
    22  	test "zotregistry.dev/zot/pkg/test/common"
    23  )
    24  
    25  func TestServerStatusCommand(t *testing.T) {
    26  	Convey("ServerStatusCommand", t, func() {
    27  		port := test.GetFreePort()
    28  		baseURL := test.GetBaseURL(port)
    29  		conf := config.New()
    30  		conf.HTTP.Port = port
    31  		conf.Storage.GC = false
    32  		defaultVal := true
    33  		conf.Extensions = &extconf.ExtensionConfig{
    34  			Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
    35  		}
    36  
    37  		ctlr := api.NewController(conf)
    38  		ctlr.Config.Storage.RootDirectory = t.TempDir()
    39  		cm := test.NewControllerManager(ctlr)
    40  
    41  		cm.StartAndWait(conf.HTTP.Port)
    42  		defer cm.StopServer()
    43  
    44  		configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"status-test","url":"%s","showspinner":false}]}`,
    45  			baseURL))
    46  		defer os.Remove(configPath)
    47  
    48  		args := []string{"status", "--config", "status-test"}
    49  		cmd := NewCliRootCmd()
    50  		buff := bytes.NewBufferString("")
    51  		cmd.SetOut(buff)
    52  		cmd.SetErr(buff)
    53  		cmd.SetArgs(args)
    54  		err := cmd.Execute()
    55  		So(err, ShouldBeNil)
    56  		space := regexp.MustCompile(`\s+`)
    57  		str := space.ReplaceAllString(buff.String(), " ")
    58  		actual := strings.TrimSpace(str)
    59  		So(actual, ShouldContainSubstring, config.ReleaseTag)
    60  		So(actual, ShouldContainSubstring, config.BinaryType)
    61  
    62  		// JSON
    63  		args = []string{"status", "--config", "status-test", "--format", "json"}
    64  		cmd = NewCliRootCmd()
    65  		buff = bytes.NewBufferString("")
    66  		cmd.SetOut(buff)
    67  		cmd.SetErr(buff)
    68  		cmd.SetArgs(args)
    69  		err = cmd.Execute()
    70  		So(err, ShouldBeNil)
    71  		space = regexp.MustCompile(`\s+`)
    72  		str = space.ReplaceAllString(buff.String(), " ")
    73  		actual = strings.TrimSpace(str)
    74  		So(actual, ShouldContainSubstring, config.ReleaseTag)
    75  		So(actual, ShouldContainSubstring, config.BinaryType)
    76  
    77  		// YAML
    78  		args = []string{"status", "--config", "status-test", "--format", "yaml"}
    79  		cmd = NewCliRootCmd()
    80  		buff = bytes.NewBufferString("")
    81  		cmd.SetOut(buff)
    82  		cmd.SetErr(buff)
    83  		cmd.SetArgs(args)
    84  		err = cmd.Execute()
    85  		So(err, ShouldBeNil)
    86  		space = regexp.MustCompile(`\s+`)
    87  		str = space.ReplaceAllString(buff.String(), " ")
    88  		actual = strings.TrimSpace(str)
    89  		So(actual, ShouldContainSubstring, config.ReleaseTag)
    90  		So(actual, ShouldContainSubstring, config.BinaryType)
    91  
    92  		// bad type
    93  		args = []string{"status", "--config", "status-test", "--format", "badType"}
    94  		cmd = NewCliRootCmd()
    95  		buff = bytes.NewBufferString("")
    96  		cmd.SetOut(buff)
    97  		cmd.SetErr(buff)
    98  		cmd.SetArgs(args)
    99  		err = cmd.Execute()
   100  		So(err, ShouldNotBeNil)
   101  	})
   102  }
   103  
   104  func TestServerStatusCommandErrors(t *testing.T) {
   105  	Convey("ServerStatusCommand", t, func() {
   106  		args := []string{"status"}
   107  		cmd := NewCliRootCmd()
   108  		buff := bytes.NewBufferString("")
   109  		cmd.SetOut(buff)
   110  		cmd.SetErr(buff)
   111  		cmd.SetArgs(args)
   112  		err := cmd.Execute()
   113  		So(err, ShouldNotBeNil)
   114  
   115  		// invalid URL
   116  		err = GetServerStatus(SearchConfig{
   117  			ServURL:      "a: ds",
   118  			ResultWriter: os.Stdout,
   119  		})
   120  		So(err, ShouldNotBeNil)
   121  
   122  		// fail Get request
   123  		err = GetServerStatus(SearchConfig{
   124  			ServURL:      "http://127.0.0.1:8000",
   125  			ResultWriter: os.Stdout,
   126  		})
   127  		So(err, ShouldBeNil)
   128  	})
   129  
   130  	Convey("HTTP errors", t, func() {
   131  		port := test.GetFreePort()
   132  		result := bytes.NewBuffer([]byte{})
   133  		searchConfig := SearchConfig{
   134  			SearchService: mockService{},
   135  			ServURL:       fmt.Sprintf("http://127.0.0.1:%v", port),
   136  			User:          "",
   137  			OutputFormat:  "text",
   138  			ResultWriter:  result,
   139  		}
   140  
   141  		Convey("v2 is Unauthorised", func() {
   142  			server := StartTestHTTPServer(HTTPRoutes{
   143  				RouteHandler{
   144  					Route: "/v2/",
   145  					HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   146  						w.WriteHeader(http.StatusUnauthorized)
   147  					},
   148  					AllowedMethods: []string{http.MethodGet},
   149  				},
   150  			}, port)
   151  			defer server.Close()
   152  
   153  			err := GetServerStatus(searchConfig)
   154  			So(err, ShouldBeNil)
   155  			So(result.String(), ShouldContainSubstring, "unauthorised access, endpoint requires valid user credentials")
   156  
   157  			// with bad user set
   158  			searchConfig.User = "test:test"
   159  			err = GetServerStatus(searchConfig)
   160  			So(err, ShouldBeNil)
   161  			So(result.String(), ShouldContainSubstring, "unauthorised access, given credentials are invalid")
   162  		})
   163  
   164  		Convey("v2 bad http status code", func() {
   165  			server := StartTestHTTPServer(HTTPRoutes{
   166  				RouteHandler{
   167  					Route: "/v2/",
   168  					HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   169  						w.WriteHeader(http.StatusInternalServerError)
   170  					},
   171  					AllowedMethods: []string{http.MethodGet},
   172  				},
   173  			}, port)
   174  			defer server.Close()
   175  
   176  			err := GetServerStatus(searchConfig)
   177  			So(err, ShouldBeNil)
   178  			So(result.String(), ShouldContainSubstring, zerr.ErrAPINotSupported.Error())
   179  		})
   180  
   181  		Convey("MGMT errors", func() {
   182  			Convey("URL not found", func() {
   183  				server := StartTestHTTPServer(HTTPRoutes{
   184  					RouteHandler{
   185  						Route: "/v2/",
   186  						HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   187  							w.WriteHeader(http.StatusOK)
   188  						},
   189  						AllowedMethods: []string{http.MethodGet},
   190  					},
   191  				}, port)
   192  				defer server.Close()
   193  
   194  				err := GetServerStatus(searchConfig)
   195  				So(err, ShouldBeNil)
   196  				So(result.String(), ShouldContainSubstring, "endpoint is not available")
   197  			})
   198  
   199  			Convey("Unauthorized Access", func() {
   200  				server := StartTestHTTPServer(HTTPRoutes{
   201  					RouteHandler{
   202  						Route: "/v2/",
   203  						HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   204  							w.WriteHeader(http.StatusOK)
   205  						},
   206  						AllowedMethods: []string{http.MethodGet},
   207  					},
   208  					RouteHandler{
   209  						Route: constants.RoutePrefix + constants.ExtMgmt,
   210  						HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   211  							w.WriteHeader(http.StatusUnauthorized)
   212  						},
   213  						AllowedMethods: []string{http.MethodGet},
   214  					},
   215  				}, port)
   216  				defer server.Close()
   217  
   218  				err := GetServerStatus(searchConfig)
   219  				So(err, ShouldBeNil)
   220  				So(result.String(), ShouldContainSubstring, "unauthorised access")
   221  			})
   222  
   223  			Convey("Bad status code", func() {
   224  				server := StartTestHTTPServer(HTTPRoutes{
   225  					RouteHandler{
   226  						Route: "/v2/",
   227  						HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   228  							w.WriteHeader(http.StatusOK)
   229  						},
   230  						AllowedMethods: []string{http.MethodGet},
   231  					},
   232  					RouteHandler{
   233  						Route: constants.RoutePrefix + constants.ExtMgmt,
   234  						HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
   235  							w.WriteHeader(http.StatusInternalServerError)
   236  						},
   237  						AllowedMethods: []string{http.MethodGet},
   238  					},
   239  				}, port)
   240  				defer server.Close()
   241  
   242  				err := GetServerStatus(searchConfig)
   243  				So(err, ShouldBeNil)
   244  				So(result.String(), ShouldContainSubstring, zerr.ErrAPINotSupported.Error())
   245  			})
   246  		})
   247  	})
   248  }