Skip to content

Commit

Permalink
move OCI/Docker-registry-related cmds from remote to their own cmd (#…
Browse files Browse the repository at this point in the history
…1908)

* improved doc strings

* fixed stray unrenamed method

* updated e2e test names

* added visual indicator of system endpoint in keyserver list

* re-added `login` capability for keyservers

* move registry-related cmds from `remote` to their own cmd

* improved visuals for insecure registries

* corrected error msgs in registry login/logout flows

* refactor: create ObtainLoginArgs() and use it from keyserver & registry CLI code
  • Loading branch information
preminger authored Jul 21, 2023
1 parent 9cf1bbe commit 925071a
Show file tree
Hide file tree
Showing 16 changed files with 768 additions and 316 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
- The keyserver-related commands that were under `remote` have been moved to
their own, dedicated `keyserver` command. Run `singularity help keyserver` for
more information.
- The commands related to OCI/Docker registries that were under `remote` have
been moved to their own, dedicated `registry` command. Run
`singularity help registry` for more information.

### New Features & Functionality

Expand Down
22 changes: 1 addition & 21 deletions cmd/internal/cli/keyserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
package cli

import (
"io"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/sylabs/singularity/docs"
Expand Down Expand Up @@ -168,25 +166,7 @@ func setKeyserver(_ *cobra.Command, _ []string) {
var KeyserverLoginCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
loginArgs := new(singularity.LoginArgs)

loginArgs.Name = args[0]

loginArgs.Username = loginUsername
loginArgs.Password = loginPassword
loginArgs.Tokenfile = loginTokenFile
loginArgs.Insecure = loginInsecure

if loginPasswordStdin {
p, err := io.ReadAll(os.Stdin)
if err != nil {
sylog.Fatalf("Failed to read password from stdin: %s", err)
}
loginArgs.Password = strings.TrimSuffix(string(p), "\n")
loginArgs.Password = strings.TrimSuffix(loginArgs.Password, "\r")
}

if err := singularity.KeyserverLogin(remoteConfig, loginArgs); err != nil {
if err := singularity.KeyserverLogin(remoteConfig, ObtainLoginArgs(args[0])); err != nil {
sylog.Fatalf("%s", err)
}
},
Expand Down
38 changes: 38 additions & 0 deletions cmd/internal/cli/loginargs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2020, Control Command Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package cli

import (
"io"
"os"
"strings"

"github.com/sylabs/singularity/internal/app/singularity"
"github.com/sylabs/singularity/pkg/sylog"
)

func ObtainLoginArgs(name string) *singularity.LoginArgs {
var loginArgs singularity.LoginArgs

loginArgs.Name = name

loginArgs.Username = loginUsername
loginArgs.Password = loginPassword
loginArgs.Tokenfile = loginTokenFile
loginArgs.Insecure = loginInsecure

if loginPasswordStdin {
p, err := io.ReadAll(os.Stdin)
if err != nil {
sylog.Fatalf("Failed to read password from stdin: %s", err)
}
loginArgs.Password = strings.TrimSuffix(string(p), "\n")
loginArgs.Password = strings.TrimSuffix(loginArgs.Password, "\r")
}

return &loginArgs
}
142 changes: 142 additions & 0 deletions cmd/internal/cli/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2020, Control Command Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package cli

import (
"github.com/spf13/cobra"
"github.com/sylabs/singularity/docs"
"github.com/sylabs/singularity/internal/app/singularity"
"github.com/sylabs/singularity/pkg/cmdline"
"github.com/sylabs/singularity/pkg/sylog"
)

// -c|--config
var registryConfigFlag = cmdline.Flag{
ID: "registryConfigFlag",
Value: &remoteConfig,
DefaultValue: remoteConfigUser,
Name: "config",
ShortHand: "c",
Usage: "path to the file holding remote endpoint configurations",
}

// -u|--username
var registryLoginUsernameFlag = cmdline.Flag{
ID: "registryLoginUsernameFlag",
Value: &loginUsername,
DefaultValue: "",
Name: "username",
ShortHand: "u",
Usage: "username to authenticate with (required for Docker/OCI registry login)",
EnvKeys: []string{"LOGIN_USERNAME"},
}

// -p|--password
var registryLoginPasswordFlag = cmdline.Flag{
ID: "registryLoginPasswordFlag",
Value: &loginPassword,
DefaultValue: "",
Name: "password",
ShortHand: "p",
Usage: "password / token to authenticate with",
EnvKeys: []string{"LOGIN_PASSWORD"},
}

// --password-stdin
var registryLoginPasswordStdinFlag = cmdline.Flag{
ID: "registryLoginPasswordStdinFlag",
Value: &loginPasswordStdin,
DefaultValue: false,
Name: "password-stdin",
Usage: "take password from standard input",
}

func init() {
addCmdInit(func(cmdManager *cmdline.CommandManager) {
cmdManager.RegisterCmd(RegistryCmd)
cmdManager.RegisterSubCmd(RegistryCmd, RegistryLoginCmd)
cmdManager.RegisterSubCmd(RegistryCmd, RegistryLogoutCmd)
cmdManager.RegisterSubCmd(RegistryCmd, RegistryListCmd)

// default location of the remote.yaml file is the user directory
cmdManager.RegisterFlagForCmd(&registryConfigFlag, RegistryCmd)

cmdManager.RegisterFlagForCmd(&registryLoginUsernameFlag, RegistryLoginCmd)
cmdManager.RegisterFlagForCmd(&registryLoginPasswordFlag, RegistryLoginCmd)
cmdManager.RegisterFlagForCmd(&registryLoginPasswordStdinFlag, RegistryLoginCmd)
})
}

// RegistryCmd singularity registry [...]
var RegistryCmd = &cobra.Command{
Run: nil,

Use: docs.RegistryUse,
Short: docs.RegistryShort,
Long: docs.RegistryLong,
Example: docs.RegistryExample,

DisableFlagsInUseLine: true,
}

// RegistryLoginCmd singularity registry login [option] <registry_url>
var RegistryLoginCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if err := singularity.RegistryLogin(remoteConfig, ObtainLoginArgs(args[0])); err != nil {
sylog.Fatalf("%s", err)
}
},

Use: docs.RegistryLoginUse,
Short: docs.RegistryLoginShort,
Long: docs.RegistryLoginLong,
Example: docs.RegistryLoginExample,

DisableFlagsInUseLine: true,
}

// RegistryLogoutCmd singularity remote logout [remoteName|serviceURI]
var RegistryLogoutCmd = &cobra.Command{
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
// default to empty string to signal to registryLogin to use default remote
name := ""
if len(args) > 0 {
name = args[0]
}

if err := singularity.RegistryLogout(remoteConfig, name); err != nil {
sylog.Fatalf("%s", err)
}
sylog.Infof("Logout succeeded")
},

Use: docs.RegistryLogoutUse,
Short: docs.RegistryLogoutShort,
Long: docs.RegistryLogoutLong,
Example: docs.RegistryLogoutExample,

DisableFlagsInUseLine: true,
}

// RegistryListCmd singularity remote list
var RegistryListCmd = &cobra.Command{
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
if err := singularity.RegistryList(remoteConfig); err != nil {
sylog.Fatalf("%s", err)
}
},

Use: docs.RegistryListUse,
Short: docs.RegistryListShort,
Long: docs.RegistryListLong,
Example: docs.RegistryListExample,

DisableFlagsInUseLine: true,
}
2 changes: 1 addition & 1 deletion cmd/internal/cli/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ var remoteLoginUsernameFlag = cmdline.Flag{
DefaultValue: "",
Name: "username",
ShortHand: "u",
Usage: "username to authenticate with (required for Docker/OCI registry login)",
Usage: "username to authenticate with",
EnvKeys: []string{"LOGIN_USERNAME"},
}

Expand Down
65 changes: 65 additions & 0 deletions docs/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2020, Control Command Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package docs

// Global content for help and man pages
const (

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// registry command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RegistryUse string = `registry [subcommand options...]`
RegistryShort string = `Manage authentication to OCI/Docker registries`
RegistryLong string = `
The 'registry' command allows you to manage authentication to standalone OCI/Docker
registries, such as 'docker://'' or 'oras://'.`
RegistryExample string = `
All group commands have their own help output:
$ singularity help registry login
$ singularity registry login`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// registry login command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RegistryLoginUse string = `login [login options...] <registry_uri>`
RegistryLoginShort string = `Login to an OCI/Docker registry`
RegistryLoginLong string = `
The 'registry login' command allows you to login to a specific OCI/Docker
registry.`
RegistryLoginExample string = `
To login in to a docker/OCI registry:
$ singularity registry login --username foo docker://docker.io
$ singularity registry login --username foo oras://myregistry.example.com
Note that many cloud OCI registries use token-based authentication. The token
should be specified as the password for login. A username is still required.
E.g. when using a standard Azure identity and token to login to an ACR
registry, the username '00000000-0000-0000-0000-000000000000' is required.
Consult your provider's documentation for details concerning their specific
login requirements.`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// registry logout command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RegistryLogoutUse string = `logout <registry_uri>`
RegistryLogoutShort string = `Logout from an OCI/Docker registry`
RegistryLogoutLong string = `
The 'registry logout' command allows you to log out from an OCI/Docker
registry.`
RegistryLogoutExample string = `
To log out from an OCI/Docker registry
$ singularity registry logout docker://docker.io`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// registry list command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RegistryListUse string = `list`
RegistryListShort string = `List all OCI credentials that are configured`
RegistryListLong string = `
The 'registry list' command lists all credentials for OCI/Docker registries
that are configured for use.`
RegistryListExample string = `
$ singularity registry list`
)
54 changes: 17 additions & 37 deletions docs/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ const (
// remote command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RemoteUse string = `remote [remote options...]`
RemoteShort string = `Manage singularity remote endpoints and OCI/Docker registry credentials`
RemoteShort string = `Manage singularity remote endpoints`
RemoteLong string = `
The 'remote' command allows you to manage Singularity remote endpoints and
OCI/Docker registry credentials through its subcommands.
The 'remote' command allows you to manage Singularity remote endpoints through
its subcommands.
A 'remote endpoint' is the Sylabs Cloud, a Singularity Enterprise installation,
or a compatible group of services. The remote endpoint is a single address,
Expand All @@ -30,12 +30,6 @@ const (
and push. You can also 'remote logout' from and 'remote remove' an endpoint that
is no longer required.
To configure credentials for OCI registries that should be used when pulling or
pushing from/to 'docker://'' or 'oras://' URIs, use the 'remote login' command
only. You do not have to 'remote add' OCI registries. To remove credentials
'remote logout' with the same URI. You do not need to 'remote remove' OCI
credentials.
The remote configuration is stored in $HOME/.singularity/remotes.yaml by default.`
RemoteExample string = `
All group commands have their own help output:
Expand Down Expand Up @@ -84,10 +78,9 @@ const (
// remote list command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RemoteListUse string = `list`
RemoteListShort string = `List all singularity remote endpoints and OCI credentials that are configured`
RemoteListShort string = `List all singularity remote endpoints that are configured`
RemoteListLong string = `
The 'remote list' command lists all remote endpoints and OCI registry
credentials configured for use.
The 'remote list' command lists all remote endpoints configured for use.
The current remote is indicated by 'YES' in the 'ACTIVE' column and can be changed
with the 'remote use' command.`
Expand All @@ -96,43 +89,30 @@ const (
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// remote login command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RemoteLoginUse string = `login [login options...] <remote_name|registry_uri>`
RemoteLoginShort string = `Login to a singularity remote endpoint or an OCI/Docker registry using credentials`
RemoteLoginUse string = `login [login options...] <remote_name>`
RemoteLoginShort string = `Login to a singularity remote endpoint`
RemoteLoginLong string = `
The 'remote login' command allows you to set credentials for a specific endpoint or
an OCI/Docker registry.
The 'remote login' command allows you to set credentials for a specific
endpoint.
If no endpoint or registry is specified, the command will login to the currently
active remote endpoint. This is cloud.sylabs.io by default.`
If no endpoint is specified, the command will login to the currently active
remote endpoint. This is cloud.sylabs.io by default.`
RemoteLoginExample string = `
To log in to an endpoint:
$ singularity remote login SylabsCloud
To login in to a docker/OCI registry:
$ singularity remote login --username foo docker://docker.io
$ singularity remote login --username foo oras://myregistry.example.com
Note that many cloud OCI registries use token based authentication. The token
should be specified as the password for login. A username is still required. E.g.
when using a standard Azure identity and token to login to an ACR registry the
username '00000000-0000-0000-0000-000000000000' is required. Consult your provider
documentation for detail of their login requirements.`
$ singularity remote login SylabsCloud`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// remote logout command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RemoteLogoutUse string = `logout <remote_name|registry_uri>`
RemoteLogoutShort string = `Log out from a singularity remote endpoint or an OCI/Docker registry`
RemoteLogoutUse string = `logout <remote_name>`
RemoteLogoutShort string = `Log out from a singularity remote endpoint`
RemoteLogoutLong string = `
The 'remote logout' command allows you to log out from a singularity specific
endpoint or an OCI/Docker registry. If no endpoint or service is specified, it
will logout from the current active remote endpoint.
endpoint. If no endpoint or service is specified, it will logout from the
currently active remote endpoint.
`
RemoteLogoutExample string = `
To log out from an endpoint
$ singularity remote logout SylabsCloud
To log out from a docker/OCI registry
$ singularity remote logout docker://docker.io`
$ singularity remote logout SylabsCloud`
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// remote status command
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Loading

0 comments on commit 925071a

Please sign in to comment.