Skip to content

Commit

Permalink
Deprecated registryProviderFlag and updated docs (#382)
Browse files Browse the repository at this point in the history
* Deprecated registryProviderFlag and updated docs

* fix linting and test errors

---------

Co-authored-by: Sami Alajrami <[email protected]>
  • Loading branch information
ToreMerkely and sami-alajrami authored Dec 10, 2024
1 parent de247ce commit 8513d91
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 219 deletions.
135 changes: 5 additions & 130 deletions cmd/kosli/cli_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"io"
"net/http"
urlPackage "net/url"
"os"
"path/filepath"
Expand All @@ -19,7 +18,6 @@ import (
"github.com/kosli-dev/cli/internal/digest"
"github.com/kosli-dev/cli/internal/gitview"
log "github.com/kosli-dev/cli/internal/logger"
"github.com/kosli-dev/cli/internal/requests"
"github.com/kosli-dev/cli/internal/utils"
cp "github.com/otiai10/copy"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -311,92 +309,6 @@ func GetFlagFromVarName(varName string) string {
return result
}

type registryProviderEndpoints struct {
mainApi string
authApi string
service string
}

func getRegistryEndpointForProvider(provider string) (*registryProviderEndpoints, error) {
switch provider {
case "dockerhub":
return &registryProviderEndpoints{
mainApi: "https://registry-1.docker.io/v2",
authApi: "https://auth.docker.io",
service: "registry.docker.io",
}, nil
case "github":
return &registryProviderEndpoints{
mainApi: "https://ghcr.io/v2",
authApi: "https://ghcr.io",
service: "ghcr.io",
}, nil

default:
return getRegistryEndpoint(provider)
}
}

func getRegistryEndpoint(url string) (*registryProviderEndpoints, error) {
url = strings.TrimPrefix(url, "https://")
url = strings.Split(url, "/")[0]

return &registryProviderEndpoints{
mainApi: "https://" + url + "/v2",
authApi: "https://" + url + "/oauth2",
service: url,
}, nil
}

// getDockerRegistryAPIToken returns a short-lived read-only api token for a docker registry api
func getDockerRegistryAPIToken(providerInfo *registryProviderEndpoints, username, password, imageName string) (string, error) {
var res *requests.HTTPResponse
var err error

if strings.Contains(providerInfo.service, "jfrog") {
url := "https://" + providerInfo.service + "/artifactory/api/security/token"

form := urlPackage.Values{}
form.Add("username", username)
form.Add("scope", "member-of-groups:readers")
form.Add("expires_in", "60")

reqParams := &requests.RequestParams{
Method: http.MethodPost,
URL: url,
Payload: form.Encode(),
Username: username,
Password: password,
AdditionalHeaders: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
}
res, err = kosliClient.Do(reqParams)
} else {
url := fmt.Sprintf("%s/token?scope=repository:%s:pull&service=%s", providerInfo.authApi, imageName, providerInfo.service)
reqParams := &requests.RequestParams{
Method: http.MethodGet,
URL: url,
Username: username,
Password: password,
}
res, err = kosliClient.Do(reqParams)
}

if err != nil {
return "", fmt.Errorf("failed to create an authentication token for the docker registry: %v %v", err, res)
}

var responseData map[string]interface{}
err = json.Unmarshal([]byte(res.Body), &responseData)
if err != nil {
return "", err
}
token := responseData["token"]
if token == nil {
token = responseData["access_token"]
}
return token.(string), nil
}

// GetSha256Digest calculates the sha256 digest of an artifact.
// Supported artifact types are: dir, file, docker
func GetSha256Digest(artifactName string, o *fingerprintOptions, logger *log.Logger) (string, error) {
Expand All @@ -410,42 +322,8 @@ func GetSha256Digest(artifactName string, o *fingerprintOptions, logger *log.Log
case "oci":
fingerprint, err = digest.OciSha256(artifactName, o.registryUsername, o.registryPassword)
case "docker":
if o.registryProvider != "" {
var providerInfo *registryProviderEndpoints
providerInfo, err = getRegistryEndpointForProvider(o.registryProvider)
if err != nil {
return "", err
}

nameSlice := strings.Split(artifactName, ":")
if len(nameSlice) < 2 {
nameSlice = append(nameSlice, "latest")
}
imageName := nameSlice[0]
imageTag := nameSlice[1]

if strings.Contains(nameSlice[0], "/") {
strSlice := strings.Split(nameSlice[0], "/")
urlOrRepo := strSlice[0]
if strings.Contains(urlOrRepo, ".") {
imageName = strings.TrimPrefix(nameSlice[0], urlOrRepo+"/")
}
}

if !strings.Contains(imageName, "/") && o.registryProvider == "dockerhub" {
imageName = fmt.Sprintf("library/%s", imageName)
}

token, err := getDockerRegistryAPIToken(providerInfo, o.registryUsername, o.registryPassword, imageName)
if err != nil {
return "", err
}

fingerprint, err = digest.RemoteDockerImageSha256(imageName, imageTag, providerInfo.mainApi, token, logger)
if err != nil {
return "", err
}

if o.registryUsername != "" {
fingerprint, err = digest.OciSha256(artifactName, o.registryUsername, o.registryPassword)
} else {
fingerprint, err = digest.DockerImageSha256(artifactName)
}
Expand Down Expand Up @@ -540,13 +418,10 @@ func ValidateAttestationArtifactArg(args []string, artifactType, inputSha256 str
// remote digest.
func ValidateRegistryFlags(cmd *cobra.Command, o *fingerprintOptions) error {
if o.artifactType != "docker" && o.artifactType != "oci" && (o.registryPassword != "" || o.registryUsername != "") {
return ErrorBeforePrintingUsage(cmd, "--registry-provider, --registry-username and registry-password are only applicable when --artifact-type is 'docker'")
}
if o.registryProvider != "" && (o.registryPassword == "" || o.registryUsername == "") {
return ErrorBeforePrintingUsage(cmd, "both --registry-username and registry-password are required when --registry-provider is used")
return ErrorBeforePrintingUsage(cmd, "--registry-username and registry-password are only applicable when --artifact-type is 'docker' or 'oci'")
}
if o.registryProvider == "" && o.artifactType != "oci" && (o.registryPassword != "" || o.registryUsername != "") {
return ErrorBeforePrintingUsage(cmd, "--registry-username and registry-password are only used when --registry-provider is used")
if (o.registryPassword == "" && o.registryUsername != "") || (o.registryPassword != "" && o.registryUsername == "") {
return ErrorBeforePrintingUsage(cmd, "--registry-username and registry-password must both be set")
}
return nil
}
Expand Down
70 changes: 1 addition & 69 deletions cmd/kosli/cli_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,45 +561,6 @@ func (suite *CliUtilsTestSuite) TestValidateArtifactArg() {
}
}

func (suite *CliUtilsTestSuite) TestGetRegistryEndpointForProvider() {
for _, t := range []struct {
name string
provider string
want *registryProviderEndpoints
expectError bool
}{
{
name: "github provider returns expected endpoints",
provider: "github",
want: &registryProviderEndpoints{
mainApi: "https://ghcr.io/v2",
authApi: "https://ghcr.io",
service: "ghcr.io",
},
},
{
name: "dockerhub provider returns expected endpoints",
provider: "dockerhub",
want: &registryProviderEndpoints{
mainApi: "https://registry-1.docker.io/v2",
authApi: "https://auth.docker.io",
service: "registry.docker.io",
},
},
} {
suite.Run(t.name, func() {
endpoints, err := getRegistryEndpointForProvider(t.provider)
if t.expectError {
require.Errorf(suite.T(), err, "error was expected but got none")
} else {
require.NoErrorf(suite.T(), err, "error was NOT expected but got %v", err)
require.Equalf(suite.T(), t.want, endpoints,
"TestGetRegistryEndpointForProvider: got %v -- want %v", t.want, endpoints)
}
})
}
}

func (suite *CliUtilsTestSuite) TestValidateRegistryFlags() {
for _, t := range []struct {
name string
Expand All @@ -610,16 +571,14 @@ func (suite *CliUtilsTestSuite) TestValidateRegistryFlags() {
name: "registry flags are valid",
options: &fingerprintOptions{
artifactType: "docker",
registryProvider: "dockerhub",
registryUsername: "user",
registryPassword: "pass",
},
},
{
name: "non-docker type with registry flags set casues an error",
name: "non-docker type with registry flags set causes an error",
options: &fingerprintOptions{
artifactType: "file",
registryProvider: "dockerhub",
registryUsername: "user",
registryPassword: "pass",
},
Expand All @@ -629,7 +588,6 @@ func (suite *CliUtilsTestSuite) TestValidateRegistryFlags() {
name: "missing username causes an error",
options: &fingerprintOptions{
artifactType: "docker",
registryProvider: "dockerhub",
registryPassword: "pass",
},
expectError: true,
Expand All @@ -638,36 +596,10 @@ func (suite *CliUtilsTestSuite) TestValidateRegistryFlags() {
name: "missing password causes an error",
options: &fingerprintOptions{
artifactType: "docker",
registryProvider: "dockerhub",
registryUsername: "user",
},
expectError: true,
},
{
name: "missing provider causes an error 1",
options: &fingerprintOptions{
artifactType: "docker",
registryUsername: "user",
registryPassword: "pass",
},
expectError: true,
},
{
name: "missing provider causes an error 2",
options: &fingerprintOptions{
artifactType: "docker",
registryUsername: "user",
},
expectError: true,
},
{
name: "missing provider causes an error 3",
options: &fingerprintOptions{
artifactType: "docker",
registryPassword: "pass",
},
expectError: true,
},
} {
suite.Run(t.name, func() {
err := ValidateRegistryFlags(&cobra.Command{}, t.options)
Expand Down
14 changes: 11 additions & 3 deletions cmd/kosli/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ plus the ability to use recursive globs "**"

const fingerprintLongDesc = fingerprintShortDesc + `
Requires ^--artifact-type^ flag to be set.
Artifact type can be one of: "file" for files, "dir" for directories, "docker" for docker images.
Artifact type can be one of: "file" for files, "dir" for directories, "oci" for container
images in registries or "docker" for local docker images.
Fingerprinting docker images can be done using the local docker daemon or the fingerprint can be fetched
Fingerprinting container images can be done using the local docker daemon or the fingerprint can be fetched
from a remote registry.
` + fingerprintDirSynopsis
Expand All @@ -36,8 +37,14 @@ kosli fingerprint --artifact-type dir mydir
# fingerprint a dir while excluding paths
kosli fingerprint --artifact-type dir --exclude logs --exclude *.exe mydir
# fingerprint a locally available docker image
# fingerprint a locally available docker image (requires docker daemon running)
kosli fingerprint --artifact-type docker nginx:latest
# fingerprint a public image from a remote registry
kosli fingerprint --artifact-type oci nginx:latest
# fingerprint a private image from a remote registry
kosli fingerprint --artifact-type oci private:latest --registry-username YourUsername --registry-password YourPassword
`

type fingerprintOptions struct {
Expand Down Expand Up @@ -74,6 +81,7 @@ func newFingerprintCmd(out io.Writer) *cobra.Command {
err = DeprecateFlags(cmd, map[string]string{
"e": "use -x instead",
})

if err != nil {
logger.Error("failed to configure deprecated flags: %v", err)
}
Expand Down
9 changes: 9 additions & 0 deletions cmd/kosli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
ghUtils "github.com/kosli-dev/cli/internal/github"
gitlabUtils "github.com/kosli-dev/cli/internal/gitlab"
"github.com/spf13/cobra"
"log"
)

// allowed commit redaction values
Expand All @@ -22,6 +23,14 @@ func addFingerprintFlags(cmd *cobra.Command, o *fingerprintOptions) {
cmd.Flags().StringVar(&o.registryUsername, "registry-username", "", registryUsernameFlag)
cmd.Flags().StringVar(&o.registryPassword, "registry-password", "", registryPasswordFlag)
cmd.Flags().StringSliceVarP(&o.excludePaths, "exclude", "x", []string{}, excludePathsFlag)

err := DeprecateFlags(cmd, map[string]string{
"registry-provider": "no longer used",
})

if err != nil {
log.Fatalf("failed to configure deprecated flags: %v", err)
}
}

func addAWSAuthFlags(cmd *cobra.Command, o *aws.AWSStaticCreds) {
Expand Down
Loading

0 comments on commit 8513d91

Please sign in to comment.