Skip to content

Commit

Permalink
feat(cli): add command to interogate the server version and other det…
Browse files Browse the repository at this point in the history
…ails

Signed-off-by: Laurentiu Niculae <[email protected]>
  • Loading branch information
laurentiuNiculae committed Aug 24, 2023
1 parent 099e3ce commit 3636575
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ exporter-minimal: modcheck build-metadata
.PHONY: test
test: $(if $(findstring ui,$(BUILD_LABELS)), ui)
test: check-skopeo $(TESTDATA) $(ORAS)
go test -failfast -tags $(BUILD_LABELS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./...
go test -failfast -tags $(BUILD_LABELS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./... -ldflags "-X zotregistry.io/zot/pkg/api/config.ReleaseTag=1.1.1 -X zotregistry.io/zot/pkg/api/config.BinaryType=test,build,tags"
rm -rf /tmp/getter*; rm -rf /tmp/trivy*
go test -failfast -tags containers_image_openpgp -v -trimpath -race -cover -coverpkg ./... -coverprofile=coverage-minimal.txt -covermode=atomic ./...
rm -rf /tmp/getter*; rm -rf /tmp/trivy*
Expand Down
3 changes: 2 additions & 1 deletion errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
ErrUnauthorizedAccess = errors.New("auth: unauthorized access. check credentials")
ErrCannotResetConfigKey = errors.New("cli: cannot reset given config key")
ErrConfigNotFound = errors.New("cli: config with the given name does not exist")
ErrNoURLProvided = errors.New("cli: no URL provided in argument or via config")
ErrNoURLProvided = errors.New("cli: no URL provided by flag or via config")
ErrIllegalConfigKey = errors.New("cli: given config key is not allowed")
ErrScanNotSupported = errors.New("search: scanning of image media type not supported")
ErrCLITimeout = errors.New("cli: Query timed out while waiting for results")
Expand Down Expand Up @@ -114,4 +114,5 @@ var (
ErrGQLEndpointNotFound = errors.New("cli: the server doesn't have a gql endpoint")
ErrGQLQueryNotSupported = errors.New("cli: query is not supported or has different arguments")
ErrBadHTTPStatusCode = errors.New("cli: the response doesn't contain the expected status code")
ErrFormatNotSupported = errors.New("cli: the given output format is not supported")
)
2 changes: 1 addition & 1 deletion pkg/api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func (c *Config) AreUserPrefsEnabled() bool {
}

func (c *Config) IsMgmtEnabled() bool {
return c.IsSearchEnabled() && c.IsUIEnabled()
return c.IsSearchEnabled()
}

func (c *Config) IsImageTrustEnabled() bool {
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8538,17 +8538,17 @@ func TestDistSpecExtensions(t *testing.T) {
So(err, ShouldBeNil)
t.Log(extensionList.Extensions)
So(len(extensionList.Extensions), ShouldEqual, 1)
So(len(extensionList.Extensions[0].Endpoints), ShouldEqual, 1)
So(len(extensionList.Extensions[0].Endpoints), ShouldEqual, 2)
So(extensionList.Extensions[0].Name, ShouldEqual, constants.BaseExtension)
So(extensionList.Extensions[0].URL, ShouldContainSubstring, "_zot.md")
So(extensionList.Extensions[0].Description, ShouldNotBeEmpty)
// Verify the endpoints below are enabled by search
So(extensionList.Extensions[0].Endpoints, ShouldContain, constants.FullSearchPrefix)
So(extensionList.Extensions[0].Endpoints, ShouldContain, constants.FullMgmt)
// Verify the endpoints below are not enabled since trust is not enabled
So(extensionList.Extensions[0].Endpoints, ShouldNotContain, constants.FullCosign)
So(extensionList.Extensions[0].Endpoints, ShouldNotContain, constants.FullNotation)
// Verify the endpoints below are not enabled since the UI is not enabled
So(extensionList.Extensions[0].Endpoints, ShouldNotContain, constants.FullMgmt)
So(extensionList.Extensions[0].Endpoints, ShouldNotContain, constants.FullUserPrefs)
})

Expand Down
1 change: 1 addition & 0 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ func enableCli(rootCmd *cobra.Command) {
rootCmd.AddCommand(NewCVESCommand(NewSearchService()))
rootCmd.AddCommand(NewRepoCommand(NewSearchService()))
rootCmd.AddCommand(NewSearchCommand(NewSearchService()))
rootCmd.AddCommand(NewServerInfoCommand())
}
10 changes: 5 additions & 5 deletions pkg/cli/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ func CheckExtEndPointQuery(config searchConfig, requiredQueries ...GQLQuery) err
searchEndPoint, _ := combineServerAndEndpointURL(*config.servURL, constants.FullSearchPrefix)

schemaQuery := `
{
__schema() {
queryType {
fields {
name
{
__schema() {
queryType {
fields {
name
args {
name
}
Expand Down
5 changes: 1 addition & 4 deletions pkg/cli/extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ func TestServeMgmtExtension(t *testing.T) {
So(found, ShouldBeTrue)
})

Convey("Mgmt disabled - UI unconfigured", t, func(c C) {
Convey("Mgmt disabled - Search unconfigured", t, func(c C) {
content := `{
"storage": {
"rootDirectory": "%s"
Expand All @@ -851,9 +851,6 @@ func TestServeMgmtExtension(t *testing.T) {
"output": "%s"
},
"extensions": {
"search": {
"enable": true
}
}
}`

Expand Down
111 changes: 111 additions & 0 deletions pkg/cli/server_info_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//go:build search
// +build search

package cli

import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/spf13/cobra"
"gopkg.in/yaml.v2"

zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/cli/cmdflags"
)

func NewServerInfoCommand() *cobra.Command {
serverInfoCmd := &cobra.Command{
Use: "server-info",
Short: "Information about the server configuration and build information",
Long: `Information about the server configuration and build information`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
searchConfig, err := GetSearchConfigFromFlags(cmd, NewSearchService())
if err != nil {
return err
}

return GetServerInfo(searchConfig)
},
}

serverInfoCmd.Flags().String(cmdflags.OutputFormatFlag, "text", "Specify the output format [text|json|yaml]")

return serverInfoCmd
}

func GetServerInfo(config searchConfig) error {
username, password := getUsernameAndPassword(*config.user)
ctx := context.Background()

mgmtEndpoint, err := combineServerAndEndpointURL(*config.servURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtMgmt))
if err != nil {
return err
}

serverInfo := ServerInfo{}

_, err = makeGETRequest(ctx, mgmtEndpoint, username, password, *config.verifyTLS, *config.debug,
&serverInfo, config.resultWriter)
if err != nil {
return err
}

outputResult, err := serverInfo.ToStringFormat(*config.outputFormat)
if err != nil {
return err
}

fmt.Fprintln(config.resultWriter, outputResult)

return nil
}

type ServerInfo struct {
DistSpecVersion string `json:"distSpecVersion" mapstructure:"distSpecVersion"`
Commit string `json:"commit" mapstructure:"commit"`
BinaryType string `json:"binaryType" mapstructure:"binaryType"`
ReleaseTag string `json:"releaseTag" mapstructure:"releaseTag"`
}

func (si *ServerInfo) ToStringFormat(format string) (string, error) {
switch format {
case "text", "":
return si.ToText()
case "json":
return si.ToJSON()
case "yaml", "yml":
return si.ToYAML()
default:
return "", zerr.ErrFormatNotSupported
}
}

func (si *ServerInfo) ToText() (string, error) {
flagsList := strings.Split(strings.Trim(si.BinaryType, "-"), "-")
flags := strings.Join(flagsList, ", ")

return fmt.Sprintf("Server Version: %s\n"+
"Dist Spec Version: %s\n"+
"Built with: %s",
si.ReleaseTag, si.DistSpecVersion, flags,
),
nil
}

func (si *ServerInfo) ToJSON() (string, error) {
blob, err := json.MarshalIndent(*si, "", " ")

return string(blob), err
}

func (si *ServerInfo) ToYAML() (string, error) {
body, err := yaml.Marshal(*si)

return string(body), err
}
59 changes: 59 additions & 0 deletions pkg/cli/server_info_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build search
// +build search

package cli //nolint:testpackage

import (
"bytes"
"fmt"
"os"
"regexp"
"strings"
"testing"

. "github.com/smartystreets/goconvey/convey"

"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/test"
)

func TestServerInfoCommand(t *testing.T) {
Convey("ServerInfoCommand", t, func() {
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}

ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)

cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"server-info-test","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)

args := []string{"server-info", "--config", "server-info-test"}
cmd := NewCliRootCmd()
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, config.ReleaseTag)
So(actual, ShouldContainSubstring, config.BinaryType)
})
}
2 changes: 2 additions & 0 deletions pkg/extensions/extension_mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type Auth struct {

type StrippedConfig struct {
DistSpecVersion string `json:"distSpecVersion" mapstructure:"distSpecVersion"`
Commit string `json:"commit" mapstructure:"commit"`
ReleaseTag string `json:"releaseTag" mapstructure:"releaseTag"`
BinaryType string `json:"binaryType" mapstructure:"binaryType"`
HTTP struct {
Auth *Auth `json:"auth,omitempty" mapstructure:"auth"`
Expand Down
6 changes: 6 additions & 0 deletions swagger/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions swagger/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,9 @@
"binaryType": {
"type": "string"
},
"commit": {
"type": "string"
},
"distSpecVersion": {
"type": "string"
},
Expand All @@ -1354,6 +1357,9 @@
"$ref": "#/definitions/extensions.Auth"
}
}
},
"releaseTag": {
"type": "string"
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,17 @@ definitions:
properties:
binaryType:
type: string
commit:
type: string
distSpecVersion:
type: string
http:
properties:
auth:
$ref: '#/definitions/extensions.Auth'
type: object
releaseTag:
type: string
type: object
github_com_opencontainers_image-spec_specs-go_v1.Descriptor:
properties:
Expand Down

0 comments on commit 3636575

Please sign in to comment.