Skip to content

Refactor: Make Error logging more user friendly for project commands #424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions cmd/harbor/root/artifact/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ func DeleteArtifactCommand() *cobra.Command {
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var err error

var projectName, repoName, reference string
if len(args) > 0 {
projectName, repoName, reference := utils.ParseProjectRepoReference(args[0])
err = api.DeleteArtifact(projectName, repoName, reference)
projectName, repoName, reference = utils.ParseProjectRepoReference(args[0])
} else {
projectName := prompt.GetProjectNameFromUser()
repoName := prompt.GetRepoNameFromUser(projectName)
reference := prompt.GetReferenceFromUser(repoName, projectName)
err = api.DeleteArtifact(projectName, repoName, reference)
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName = prompt.GetRepoNameFromUser(projectName)
reference = prompt.GetReferenceFromUser(repoName, projectName)
}

err = api.DeleteArtifact(projectName, repoName, reference)
if err != nil {
log.Errorf("failed to delete an artifact: %v", err)
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/harbor/root/artifact/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ func ListArtifactCommand() *cobra.Command {
if len(args) > 0 {
projectName, repoName = utils.ParseProjectRepo(args[0])
} else {
projectName = prompt.GetProjectNameFromUser()
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
return fmt.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName = prompt.GetRepoNameFromUser(projectName)
}

Expand Down
32 changes: 20 additions & 12 deletions cmd/harbor/root/artifact/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,19 @@ func StartScanArtifactCommand() *cobra.Command {
Example: `harbor artifact scan start <project>/<repository>/<reference>`,
Run: func(cmd *cobra.Command, args []string) {
var err error
var projectName, repoName, reference string

if len(args) > 0 {
projectName, repoName, reference := utils.ParseProjectRepoReference(args[0])
err = api.StartScanArtifact(projectName, repoName, reference)
projectName, repoName, reference = utils.ParseProjectRepoReference(args[0])
} else {
projectName := prompt.GetProjectNameFromUser()
repoName := prompt.GetRepoNameFromUser(projectName)
reference := prompt.GetReferenceFromUser(repoName, projectName)
err = api.StartScanArtifact(projectName, repoName, reference)
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName = prompt.GetRepoNameFromUser(projectName)
reference = prompt.GetReferenceFromUser(repoName, projectName)
}
err = api.StartScanArtifact(projectName, repoName, reference)
if err != nil {
log.Errorf("failed to start scan of artifact: %v", err)
}
Expand All @@ -72,16 +75,21 @@ func StopScanArtifactCommand() *cobra.Command {
Example: `harbor artifact scan stop <project>/<repository>/<reference>`,
Run: func(cmd *cobra.Command, args []string) {
var err error
var projectName, repoName, reference string

if len(args) > 0 {
projectName, repoName, reference := utils.ParseProjectRepoReference(args[0])
err = api.StopScanArtifact(projectName, repoName, reference)
projectName, repoName, reference = utils.ParseProjectRepoReference(args[0])
} else {
projectName := prompt.GetProjectNameFromUser()
repoName := prompt.GetRepoNameFromUser(projectName)
reference := prompt.GetReferenceFromUser(repoName, projectName)
err = api.StopScanArtifact(projectName, repoName, reference)
var projectName string
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName = prompt.GetRepoNameFromUser(projectName)
reference = prompt.GetReferenceFromUser(repoName, projectName)
}

err = api.StopScanArtifact(projectName, repoName, reference)
if err != nil {
log.Errorf("failed to stop scan of artifact: %v", err)
}
Expand Down
17 changes: 14 additions & 3 deletions cmd/harbor/root/artifact/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ func CreateTagsCmd() *cobra.Command {
err = api.CreateTag(projectName, repoName, reference, tag)
} else {
var tagName string
projectName := prompt.GetProjectNameFromUser()
var projectName string
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName := prompt.GetRepoNameFromUser(projectName)
reference := prompt.GetReferenceFromUser(repoName, projectName)
create.CreateTagView(&tagName)
Expand Down Expand Up @@ -83,7 +87,10 @@ func ListTagsCmd() *cobra.Command {
if len(args) > 0 {
projectName, repoName, reference = utils.ParseProjectRepoReference(args[0])
} else {
projectName = prompt.GetProjectNameFromUser()
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName = prompt.GetRepoNameFromUser(projectName)
reference = prompt.GetReferenceFromUser(repoName, projectName)
}
Expand Down Expand Up @@ -124,7 +131,11 @@ func DeleteTagsCmd() *cobra.Command {
tag := args[1]
err = api.DeleteTag(projectName, repoName, reference, tag)
} else {
projectName := prompt.GetProjectNameFromUser()
var projectName string
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
repoName := prompt.GetRepoNameFromUser(projectName)
reference := prompt.GetReferenceFromUser(repoName, projectName)
tag := prompt.GetTagFromUser(repoName, projectName, reference)
Expand Down
6 changes: 5 additions & 1 deletion cmd/harbor/root/artifact/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ func ViewArtifactCommmand() *cobra.Command {
if len(args) > 0 {
projectName, repoName, reference = utils.ParseProjectRepoReference(args[0])
} else {
projectName = prompt.GetProjectNameFromUser()
projectName, err = prompt.GetProjectNameFromUser()
if err != nil {
log.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
return
}
repoName = prompt.GetRepoNameFromUser(projectName)
reference = prompt.GetReferenceFromUser(repoName, projectName)
}
Expand Down
16 changes: 8 additions & 8 deletions cmd/harbor/root/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package root

import (
"fmt"
"io"
"time"

"github.com/goharbor/harbor-cli/cmd/harbor/root/artifact"
Expand Down Expand Up @@ -59,17 +60,16 @@ harbor help
utils.InitConfig(cfgFile, userSpecifiedConfig)

// Conditionally set the timestamp format only in verbose mode
formatter := &logrus.TextFormatter{}

if verbose {
logrus.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
TimestampFormat: time.RFC3339,
})
formatter.FullTimestamp = true
formatter.TimestampFormat = time.RFC3339
logrus.SetLevel(logrus.DebugLevel)
} else {
// No timestamp format for non-verbose
logrus.SetFormatter(&logrus.TextFormatter{
DisableTimestamp: true,
})
logrus.SetOutput(io.Discard)
}
logrus.SetFormatter(formatter)

return nil
},
Expand Down
51 changes: 33 additions & 18 deletions cmd/harbor/root/project/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
package project

import (
"fmt"

"github.com/goharbor/harbor-cli/pkg/api"
"github.com/goharbor/harbor-cli/pkg/utils"
"github.com/goharbor/harbor-cli/pkg/views/project/create"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -28,32 +31,42 @@ func CreateProjectCommand() *cobra.Command {
Use: "create [project name]",
Short: "create project",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
var err error
createView := &create.CreateView{
ProjectName: opts.ProjectName,
Public: opts.Public,
RegistryID: opts.RegistryID,
StorageLimit: opts.StorageLimit,
ProxyCache: false,
}
var ProjectName string
if len(args) > 0 {
opts.ProjectName = args[0]
}

if opts.ProxyCache && opts.RegistryID == "" {
log.Errorf("Use the --registry-id flag with a registry ID")
} else {
err = api.CreateProject(opts)
}
if opts.ProxyCache && opts.RegistryID == "" {
return fmt.Errorf("Error: Proxy cache selected but no registry ID provided. Use --registry-id.")
}

if opts.ProjectName != "" {
log.Debug("Attempting to create project using flags...")
err = api.CreateProject(opts)
ProjectName = opts.ProjectName
} else {
log.Debug("No project name provided. Switching to interactive view...")
createView := &create.CreateView{
ProjectName: opts.ProjectName,
Public: opts.Public,
RegistryID: opts.RegistryID,
StorageLimit: opts.StorageLimit,
ProxyCache: opts.ProxyCache,
}

err = createProjectView(createView)
ProjectName = createView.ProjectName
}

if err != nil {
log.Errorf("failed to create project: %v", err)
return fmt.Errorf("failed to create project: %v", utils.ParseHarborErrorMsg(err))
}
},
}

fmt.Printf("Project '%s' created successfully\n", ProjectName)
return nil
}}

flags := cmd.Flags()
flags.BoolVarP(&opts.Public, "public", "", false, "Project is public or private")
Expand All @@ -74,7 +87,9 @@ func createProjectView(createView *create.CreateView) error {
}
}

create.CreateProjectView(createView)

err := create.CreateProjectView(createView)
if err != nil {
return err
}
return api.CreateProject(*createView)
}
95 changes: 70 additions & 25 deletions cmd/harbor/root/project/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,70 +14,115 @@
package project

import (
"fmt"
"strconv"
"sync"

"github.com/goharbor/harbor-cli/pkg/api"
"github.com/goharbor/harbor-cli/pkg/prompt"
"github.com/goharbor/harbor-cli/pkg/utils"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

// DeleteProjectCommand creates a new `harbor project delete` command
// DeleteProjectCommand creates a new `harbor delete project` command
func DeleteProjectCommand() *cobra.Command {
var forceDelete bool
var projectID string

cmd := &cobra.Command{
Use: "delete",
Short: "Delete project by name or ID",
Example: "harbor project delete [projectname] or harbor project delete --project-id [projectid]",
Long: "Delete project by name or ID. If no arguments are provided, it will prompt for the project name. Use --project-id to specify the project ID directly. The --force flag will delete all repositories and artifacts within the project.",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
Example: "harbor project delete [projectname1] [projectname2] or harbor project delete --project-id [projectid]",
Long: "Delete project by name or ID. Multiple projects can be deleted by providing their names as arguments. If no arguments are provided, it will prompt for the project name. Use --project-id to specify the project ID for single project directly. The --force flag will delete all repositories and artifacts within the project.",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
var wg sync.WaitGroup
errChan := make(chan error, len(args))
var mu sync.Mutex

successfulDeletes := []string{}
failedDeletes := map[string]string{}

if projectID != "" {
if len(args) > 0 {
return fmt.Errorf("--project-id cannot be used with additional arguments")
}
if _, err := strconv.Atoi(projectID); err != nil {
return fmt.Errorf("--project-id must be a numeric value")
}
}

if projectID != "" {
log.Debugf("Deleting project with ID: %s", projectID)
wg.Add(1)
go func(id string) {
defer wg.Done()
if err := api.DeleteProject(id, forceDelete, true); err != nil {
errChan <- err
mu.Lock()
failedDeletes[id] = utils.ParseHarborErrorMsg(err)
mu.Unlock()
} else {
mu.Lock()
successfulDeletes = append(successfulDeletes, id)
mu.Unlock()
}
}(projectID)
} else if len(args) > 0 {
// Delete by project name from args
log.Debugf("Deleting %d projects from args...", len(args))
for _, projectName := range args {
pn := projectName
log.Debugf("Initiating delete for project: %s", pn)
wg.Add(1)
go func(name string) {
go func(projectName string) {
defer wg.Done()
if err := api.DeleteProject(name, forceDelete, false); err != nil {
errChan <- err
log.Debugf("Deleting project '%s' with force=%v", projectName, forceDelete)
if err := api.DeleteProject(projectName, forceDelete, false); err != nil {
mu.Lock()
failedDeletes[projectName] = utils.ParseHarborErrorMsg(err)
mu.Unlock()
} else {
mu.Lock()
successfulDeletes = append(successfulDeletes, projectName)
mu.Unlock()
}
}(projectName)
}(pn)
}
} else {
projectName := prompt.GetProjectNameFromUser()
// If no arguments provided, prompt user for project name
log.Debug("No arguments provided. Prompting user for project name.")
projectName, err := prompt.GetProjectNameFromUser()
if err != nil {
return fmt.Errorf("failed to get project name: %v", utils.ParseHarborErrorMsg(err))
}
log.Debugf("User input project: %s", projectName)
log.Debugf("Deleting project '%s' with force=%v", projectName, forceDelete)
if err := api.DeleteProject(projectName, forceDelete, false); err != nil {
log.Errorf("failed to delete project: %v", err)
return fmt.Errorf("failed to delete project: %v", utils.ParseHarborErrorMsg(err))
}
fmt.Printf("Project '%s' deleted successfully\n", projectName)
return nil
}

go func() {
wg.Wait()
close(errChan)
}()
wg.Wait()

var finalErr error
for err := range errChan {
if finalErr == nil {
finalErr = err
} else {
log.Errorf("Error: %v", err)
if len(successfulDeletes) > 0 {
fmt.Println("Successfully deleted projects:")
for _, name := range successfulDeletes {
fmt.Printf(" - %s\n", name)
}
}
if finalErr != nil {
log.Errorf("failed to delete some projects: %v", finalErr)

if len(failedDeletes) > 0 {
fmt.Println("Failed to delete projects:")
for name, reason := range failedDeletes {
fmt.Printf(" - %s: %s\n", name, reason)
}
return fmt.Errorf("failed to delete %d project(s)", len(failedDeletes))
}

log.Debug("All requested projects deleted successfully.")
return nil
},
}

Expand Down
Loading