diff --git a/cmd/harbor/root/cmd.go b/cmd/harbor/root/cmd.go index 33509cbf8..48690b675 100644 --- a/cmd/harbor/root/cmd.go +++ b/cmd/harbor/root/cmd.go @@ -23,6 +23,7 @@ import ( "github.com/goharbor/harbor-cli/cmd/harbor/root/context" "github.com/goharbor/harbor-cli/cmd/harbor/root/cve" "github.com/goharbor/harbor-cli/cmd/harbor/root/instance" + "github.com/goharbor/harbor-cli/cmd/harbor/root/jobservice" "github.com/goharbor/harbor-cli/cmd/harbor/root/labels" "github.com/goharbor/harbor-cli/cmd/harbor/root/ldap" "github.com/goharbor/harbor-cli/cmd/harbor/root/project" @@ -203,6 +204,10 @@ harbor help cmd.GroupID = "system" root.AddCommand(cmd) + cmd = jobservice.JobService() + cmd.GroupID = "system" + root.AddCommand(cmd) + // Utils cmd = versionCommand() cmd.GroupID = "utils" diff --git a/cmd/harbor/root/jobservice/cmd.go b/cmd/harbor/root/jobservice/cmd.go new file mode 100644 index 000000000..74ddb20f5 --- /dev/null +++ b/cmd/harbor/root/jobservice/cmd.go @@ -0,0 +1,41 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package jobservice + +import ( + "github.com/goharbor/harbor-cli/cmd/harbor/root/jobservice/jobs" + "github.com/goharbor/harbor-cli/cmd/harbor/root/jobservice/pools" + "github.com/goharbor/harbor-cli/cmd/harbor/root/jobservice/workers" + "github.com/spf13/cobra" +) + +// JobService creates the jobservice command +func JobService() *cobra.Command { + cmd := &cobra.Command{ + Use: "jobservice", + Short: "Manage Harbor job service (admin only)", + Long: `Manage Harbor job service components including worker pools, job queues, schedules, and job logs. +This requires system admin privileges. + +Use "harbor jobservice [command] --help" for detailed examples and flags per subcommand.`, + } + + cmd.AddCommand( + pools.PoolsCommand(), + workers.WorkersCommand(), + jobs.JobsCommand(), + ) + + return cmd +} diff --git a/cmd/harbor/root/jobservice/jobs/log.go b/cmd/harbor/root/jobservice/jobs/log.go new file mode 100644 index 000000000..c9939157c --- /dev/null +++ b/cmd/harbor/root/jobservice/jobs/log.go @@ -0,0 +1,77 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package jobs + +import ( + "fmt" + + "github.com/goharbor/harbor-cli/pkg/api" + jobserviceutils "github.com/goharbor/harbor-cli/pkg/utils/jobservice" + "github.com/spf13/cobra" +) + +// JobsCommand creates the jobs subcommand +func JobsCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "jobs", + Short: "Manage job logs (view by job ID)", + Long: "View logs for specific jobs.", + } + + cmd.AddCommand(LogCommand()) + + return cmd +} + +// LogCommand retrieves and displays job logs +func LogCommand() *cobra.Command { + var jobID string + + cmd := &cobra.Command{ + Use: "log", + Short: "View a job log (--job-id required)", + Long: "Display the log for a specific job by job ID.", + Example: "harbor jobservice jobs log --job-id abc123def456", + RunE: func(cmd *cobra.Command, args []string) error { + if jobID == "" { + return fmt.Errorf("--job-id must be specified") + } + + fmt.Printf("Retrieving log for job %s...\n\n", jobID) + + log, err := api.GetJobLog(jobID) + if err != nil { + return jobserviceutils.FormatScheduleError("failed to retrieve job log", err, "authenticated") + } + + if log == "" { + fmt.Println("No log content available for this job.") + return nil + } + + fmt.Println("=== Job Log ===") + fmt.Println(log) + fmt.Println("=== End of Log ===") + return nil + }, + } + + flags := cmd.Flags() + flags.StringVar(&jobID, "job-id", "", "Job ID to fetch log for (required)") + if err := cmd.MarkFlagRequired("job-id"); err != nil { + panic(err) + } + + return cmd +} diff --git a/cmd/harbor/root/jobservice/jobs/log_test.go b/cmd/harbor/root/jobservice/jobs/log_test.go new file mode 100644 index 000000000..52469ef92 --- /dev/null +++ b/cmd/harbor/root/jobservice/jobs/log_test.go @@ -0,0 +1,44 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jobs + +import ( + "strings" + "testing" +) + +func TestJobsCommandIncludesLogSubcommand(t *testing.T) { + cmd := JobsCommand() + + if cmd.Use != "jobs" { + t.Fatalf("expected command use to be jobs, got %s", cmd.Use) + } + + if len(cmd.Commands()) != 1 { + t.Fatalf("expected one subcommand, got %d", len(cmd.Commands())) + } + + if got := cmd.Commands()[0].Name(); got != "log" { + t.Fatalf("expected log subcommand, got %s", got) + } +} + +func TestLogCommandRequiresJobID(t *testing.T) { + cmd := LogCommand() + cmd.SetArgs([]string{}) + if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "required flag(s) \"job-id\" not set") { + t.Fatalf("expected job-id validation error, got %v", err) + } +} diff --git a/cmd/harbor/root/jobservice/pools/list.go b/cmd/harbor/root/jobservice/pools/list.go new file mode 100644 index 000000000..abf654ee7 --- /dev/null +++ b/cmd/harbor/root/jobservice/pools/list.go @@ -0,0 +1,75 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package pools + +import ( + "fmt" + + "github.com/goharbor/harbor-cli/pkg/api" + "github.com/goharbor/harbor-cli/pkg/utils" + jobserviceutils "github.com/goharbor/harbor-cli/pkg/utils/jobservice" + poolviews "github.com/goharbor/harbor-cli/pkg/views/jobservice/pools" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// PoolsCommand creates the pools subcommand. +func PoolsCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "pools", + Aliases: []string{"pool"}, + Short: "Manage worker pools (list available pools)", + Long: `List and manage worker pools for the Harbor job service. + +Use 'list' to view all worker pools. + +Examples: + harbor jobservice pools list + harbor jobservice pool list`, + } + + cmd.AddCommand(ListCommand()) + return cmd +} + +// ListCommand lists all worker pools. +func ListCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List all worker pools", + Long: "Display all worker pools with their details.", + Example: "harbor jobservice pools list\nharbor jobservice pool list", + RunE: func(cmd *cobra.Command, args []string) error { + response, err := api.GetWorkerPools() + if err != nil { + return jobserviceutils.FormatScheduleError("failed to retrieve worker pools", err, "ActionList") + } + + if response == nil || response.Payload == nil || len(response.Payload) == 0 { + fmt.Println("No worker pools found.") + return nil + } + + formatFlag := viper.GetString("output-format") + if formatFlag != "" { + return utils.PrintFormat(response.Payload, formatFlag) + } + + poolviews.ListPools(response.Payload) + return nil + }, + } + + return cmd +} diff --git a/cmd/harbor/root/jobservice/pools/list_test.go b/cmd/harbor/root/jobservice/pools/list_test.go new file mode 100644 index 000000000..679db6717 --- /dev/null +++ b/cmd/harbor/root/jobservice/pools/list_test.go @@ -0,0 +1,45 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pools + +import "testing" + +func TestPoolsCommandIncludesListSubcommand(t *testing.T) { + cmd := PoolsCommand() + + if cmd.Use != "pools" { + t.Fatalf("expected command use to be pools, got %s", cmd.Use) + } + + if len(cmd.Commands()) != 1 { + t.Fatalf("expected one subcommand, got %d", len(cmd.Commands())) + } + + if got := cmd.Commands()[0].Name(); got != "list" { + t.Fatalf("expected list subcommand, got %s", got) + } +} + +func TestListCommandName(t *testing.T) { + cmd := ListCommand() + + if cmd.Name() != "list" { + t.Fatalf("expected list command, got %s", cmd.Name()) + } + + if cmd.Short == "" { + t.Fatal("expected short description to be populated") + } +} diff --git a/cmd/harbor/root/jobservice/workers/free.go b/cmd/harbor/root/jobservice/workers/free.go new file mode 100644 index 000000000..02d42e9fe --- /dev/null +++ b/cmd/harbor/root/jobservice/workers/free.go @@ -0,0 +1,73 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package workers + +import ( + "fmt" + + "github.com/goharbor/harbor-cli/pkg/api" + jobserviceutils "github.com/goharbor/harbor-cli/pkg/utils/jobservice" + "github.com/spf13/cobra" +) + +// FreeCommand frees a worker by stopping the running job on it. +func FreeCommand() *cobra.Command { + var jobID string + + cmd := &cobra.Command{ + Use: "free", + Short: "Free one worker (--job-id required)", + Long: "Stop a running job by job ID to free its worker.", + Example: "harbor jobservice workers free --job-id abc123", + RunE: func(cmd *cobra.Command, args []string) error { + if jobID == "" { + return fmt.Errorf("--job-id is required") + } + + err := api.StopRunningJob(jobID) + if err != nil { + return jobserviceutils.FormatScheduleError("failed to free worker", err, "ActionStop") + } + + fmt.Printf("Worker job %q stopped successfully.\n", jobID) + return nil + }, + } + + cmd.Flags().StringVar(&jobID, "job-id", "", "Running job ID to stop") + + return cmd +} + +// FreeAllCommand frees all busy workers by stopping all running jobs. +func FreeAllCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "free-all", + Short: "Free all busy workers (job-id=all)", + Long: "Stop all running jobs to free all busy workers.", + Example: "harbor jobservice workers free-all", + RunE: func(cmd *cobra.Command, args []string) error { + err := api.StopRunningJob("all") + if err != nil { + return jobserviceutils.FormatScheduleError("failed to free all workers", err, "ActionStop") + } + + fmt.Println("All busy workers were freed successfully.") + return nil + }, + } + + return cmd +} diff --git a/cmd/harbor/root/jobservice/workers/list.go b/cmd/harbor/root/jobservice/workers/list.go new file mode 100644 index 000000000..44bbfc71e --- /dev/null +++ b/cmd/harbor/root/jobservice/workers/list.go @@ -0,0 +1,130 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package workers + +import ( + "fmt" + + "github.com/goharbor/harbor-cli/pkg/api" + "github.com/goharbor/harbor-cli/pkg/utils" + jobserviceutils "github.com/goharbor/harbor-cli/pkg/utils/jobservice" + workersviews "github.com/goharbor/harbor-cli/pkg/views/jobservice/workers" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// ListCommand lists all workers +func ListCommand() *cobra.Command { + var poolID string + var allPools bool + var poolAll bool + var page int64 = 1 + var pageSize int64 = 20 + + cmd := &cobra.Command{ + Use: "list [POOL_ID]", + Short: "List workers (supports --page and --page-size)", + Long: `List job service workers. + +Supported listing modes: + - All workers (default): no POOL_ID or --pool all + - Specific pool workers: provide [POOL_ID] or --pool + - Compatibility mode: --pool-all (same as --pool all) + +Pagination: + - --page selects the 1-based page number + - --page-size controls how many workers are shown per page + +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool default + harbor jobservice workers list --page 2 --page-size 20 + harbor jobservice workers list default + harbor jobservice worker list 72327cf790564e45b7c89a2d`, + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return listWorkers(cmd, args, poolID, poolAll, page, pageSize) + }, + } + + flags := cmd.Flags() + flags.StringVar(&poolID, "pool", "", "Worker pool ID (use 'all' for all pools)") + flags.BoolVar(&allPools, "all", false, "List workers from all pools") + flags.BoolVar(&poolAll, "pool-all", false, "List workers from all pools (compatibility alias for --pool all)") + flags.Int64Var(&page, "page", 1, "Page number") + flags.Int64Var(&pageSize, "page-size", 20, "Number of workers per page") + + return cmd +} + +func listWorkers(cmd *cobra.Command, args []string, poolFlag string, poolAll bool, page, pageSize int64) error { + if page < 1 { + return fmt.Errorf("page must be >= 1") + } + if pageSize < 1 { + return fmt.Errorf("page-size must be >= 1") + } + + resolvedPoolID := "all" + allPools, _ := cmd.Flags().GetBool("all") + + if allPools || poolAll { + resolvedPoolID = "all" + } + + if poolFlag != "" { + resolvedPoolID = poolFlag + } + + if len(args) > 0 { + if poolFlag != "" || allPools || poolAll { + return fmt.Errorf("pool ID provided both as argument and flag; use only one form") + } + resolvedPoolID = args[0] + } + + resp, err := api.GetWorkers(resolvedPoolID) + if err != nil { + return jobserviceutils.FormatScheduleError("failed to get workers", err, "ActionList") + } + + if resp == nil || resp.Payload == nil || len(resp.Payload) == 0 { + fmt.Println("No workers found.") + return nil + } + + totalCount := int64(len(resp.Payload)) + start := (page - 1) * pageSize + if start >= totalCount { + fmt.Println("No workers found.") + return nil + } + + end := start + pageSize + if end > totalCount { + end = totalCount + } + + pageWorkers := resp.Payload[int(start):int(end)] + + formatFlag := viper.GetString("output-format") + if formatFlag != "" { + return utils.PrintFormat(pageWorkers, formatFlag) + } + + workersviews.ListWorkers(pageWorkers, page, pageSize, totalCount) + return nil +} diff --git a/cmd/harbor/root/jobservice/workers/list_test.go b/cmd/harbor/root/jobservice/workers/list_test.go new file mode 100644 index 000000000..25b1423d7 --- /dev/null +++ b/cmd/harbor/root/jobservice/workers/list_test.go @@ -0,0 +1,71 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package workers + +import ( + "errors" + "strings" + "testing" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" + jobserviceutils "github.com/goharbor/harbor-cli/pkg/utils/jobservice" +) + +func TestListCommandAddsPaginationFlags(t *testing.T) { + cmd := ListCommand() + + if got := cmd.Flags().Lookup("page").DefValue; got != "1" { + t.Fatalf("expected default page to be 1, got %s", got) + } + + if got := cmd.Flags().Lookup("page-size").DefValue; got != "20" { + t.Fatalf("expected default page-size to be 20, got %s", got) + } +} + +func TestListWorkersPaginationValidation(t *testing.T) { + if err := listWorkers(ListCommand(), nil, "", false, 0, 20); err == nil || !strings.Contains(err.Error(), "page must be >= 1") { + t.Fatalf("expected page validation error, got %v", err) + } + + if err := listWorkers(ListCommand(), nil, "", false, 1, 0); err == nil || !strings.Contains(err.Error(), "page-size must be >= 1") { + t.Fatalf("expected page-size validation error, got %v", err) + } +} + +func TestListWorkersPageWindow(t *testing.T) { + workers := []*models.Worker{ + {ID: "worker-1"}, + {ID: "worker-2"}, + {ID: "worker-3"}, + } + + page := int64(2) + pageSize := int64(1) + start := int((page - 1) * pageSize) + end := int(page * pageSize) + pageWorkers := workers[start:end] + + if len(pageWorkers) != 1 || pageWorkers[0].ID != "worker-2" { + t.Fatalf("expected second page to contain worker-2, got %#v", pageWorkers) + } +} + +func TestFormatWorkerActionError(t *testing.T) { + got := jobserviceutils.FormatScheduleError("failed to free worker", errors.New("[POST /jobservice/jobs][404] (status 404)"), "ActionStop") + if got == nil || !strings.Contains(got.Error(), "resource not found or not accessible in current context") { + t.Fatalf("expected 404 mapping, got %v", got) + } +} diff --git a/cmd/harbor/root/jobservice/workers/workers.go b/cmd/harbor/root/jobservice/workers/workers.go new file mode 100644 index 000000000..1f3f84091 --- /dev/null +++ b/cmd/harbor/root/jobservice/workers/workers.go @@ -0,0 +1,48 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package workers + +import ( + "github.com/spf13/cobra" +) + +// WorkersCommand creates the workers subcommand +func WorkersCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "workers", + Aliases: []string{"worker"}, + Short: "Manage workers (list all/by pool, free, free-all)", + Long: `Manage job service workers using the job service API. + +Use 'list' to view workers from all pools or a specific pool. +Use 'free' and 'free-all' to stop running jobs and release busy workers. + +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool + harbor jobservice worker list + harbor jobservice workers free --job-id + harbor jobservice workers free-all`, + } + + cmd.AddCommand( + ListCommand(), + FreeCommand(), + FreeAllCommand(), + ) + + return cmd +} diff --git a/doc/cli-docs/harbor-jobservice-jobs-log.md b/doc/cli-docs/harbor-jobservice-jobs-log.md new file mode 100644 index 000000000..f41fd3e71 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-jobs-log.md @@ -0,0 +1,43 @@ +--- +title: harbor jobservice jobs log +weight: 35 +--- +## harbor jobservice jobs log + +### Description + +##### View a job log (--job-id required) + +### Synopsis + +Display the log for a specific job by job ID. + +```sh +harbor jobservice jobs log [flags] +``` + +### Examples + +```sh +harbor jobservice jobs log --job-id abc123def456 +``` + +### Options + +```sh + -h, --help help for log + --job-id string Job ID to fetch log for (required) +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice jobs](harbor-jobservice-jobs.md) - Manage job logs (view by job ID) + diff --git a/doc/cli-docs/harbor-jobservice-jobs.md b/doc/cli-docs/harbor-jobservice-jobs.md new file mode 100644 index 000000000..652b4d6a3 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-jobs.md @@ -0,0 +1,33 @@ +--- +title: harbor jobservice jobs +weight: 50 +--- +## harbor jobservice jobs + +### Description + +##### Manage job logs (view by job ID) + +### Synopsis + +View logs for specific jobs. + +### Options + +```sh + -h, --help help for jobs +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice](harbor-jobservice.md) - Manage Harbor job service (admin only) +* [harbor jobservice jobs log](harbor-jobservice-jobs-log.md) - View a job log (--job-id required) + diff --git a/doc/cli-docs/harbor-jobservice-pools-list.md b/doc/cli-docs/harbor-jobservice-pools-list.md new file mode 100644 index 000000000..252902e3a --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-pools-list.md @@ -0,0 +1,43 @@ +--- +title: harbor jobservice pools list +weight: 70 +--- +## harbor jobservice pools list + +### Description + +##### List all worker pools + +### Synopsis + +Display all worker pools with their details. + +```sh +harbor jobservice pools list [flags] +``` + +### Examples + +```sh +harbor jobservice pools list +harbor jobservice pool list +``` + +### Options + +```sh + -h, --help help for list +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice pools](harbor-jobservice-pools.md) - Manage worker pools (list available pools) + diff --git a/doc/cli-docs/harbor-jobservice-pools.md b/doc/cli-docs/harbor-jobservice-pools.md new file mode 100644 index 000000000..289740979 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-pools.md @@ -0,0 +1,39 @@ +--- +title: harbor jobservice pools +weight: 45 +--- +## harbor jobservice pools + +### Description + +##### Manage worker pools (list available pools) + +### Synopsis + +List and manage worker pools for the Harbor job service. + +Use 'list' to view all worker pools. + +Examples: + harbor jobservice pools list + harbor jobservice pool list + +### Options + +```sh + -h, --help help for pools +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice](harbor-jobservice.md) - Manage Harbor job service (admin only) +* [harbor jobservice pools list](harbor-jobservice-pools-list.md) - List all worker pools + diff --git a/doc/cli-docs/harbor-jobservice-workers-free-all.md b/doc/cli-docs/harbor-jobservice-workers-free-all.md new file mode 100644 index 000000000..9451610aa --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-workers-free-all.md @@ -0,0 +1,42 @@ +--- +title: harbor jobservice workers free all +weight: 40 +--- +## harbor jobservice workers free-all + +### Description + +##### Free all busy workers (job-id=all) + +### Synopsis + +Stop all running jobs to free all busy workers. + +```sh +harbor jobservice workers free-all [flags] +``` + +### Examples + +```sh +harbor jobservice workers free-all +``` + +### Options + +```sh + -h, --help help for free-all +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice workers](harbor-jobservice-workers.md) - Manage workers (list all/by pool, free, free-all) + diff --git a/doc/cli-docs/harbor-jobservice-workers-free.md b/doc/cli-docs/harbor-jobservice-workers-free.md new file mode 100644 index 000000000..4442686d8 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-workers-free.md @@ -0,0 +1,43 @@ +--- +title: harbor jobservice workers free +weight: 40 +--- +## harbor jobservice workers free + +### Description + +##### Free one worker (--job-id required) + +### Synopsis + +Stop a running job by job ID to free its worker. + +```sh +harbor jobservice workers free [flags] +``` + +### Examples + +```sh +harbor jobservice workers free --job-id abc123 +``` + +### Options + +```sh + -h, --help help for free + --job-id string Running job ID to stop +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice workers](harbor-jobservice-workers.md) - Manage workers (list all/by pool, free, free-all) + diff --git a/doc/cli-docs/harbor-jobservice-workers-list.md b/doc/cli-docs/harbor-jobservice-workers-list.md new file mode 100644 index 000000000..17caecd33 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-workers-list.md @@ -0,0 +1,58 @@ +--- +title: harbor jobservice workers list +weight: 10 +--- +## harbor jobservice workers list + +### Description + +##### List workers (supports --page and --page-size) + +### Synopsis + +List job service workers. + +Supported listing modes: + - All workers (default): no POOL_ID or --pool all + - Specific pool workers: provide [POOL_ID] or --pool + - Compatibility mode: --pool-all (same as --pool all) + +Pagination: + - --page selects the 1-based page number + - --page-size controls how many workers are shown per page + +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool default + harbor jobservice workers list --page 2 --page-size 20 + harbor jobservice workers list default + harbor jobservice worker list 72327cf790564e45b7c89a2d + +```sh +harbor jobservice workers list [POOL_ID] [flags] +``` + +### Options + +```sh + --all List workers from all pools + -h, --help help for list + --page int Page number (default 1) + --page-size int Number of workers per page (default 20) + --pool string Worker pool ID (use 'all' for all pools) + --pool-all List workers from all pools (compatibility alias for --pool all) +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice workers](harbor-jobservice-workers.md) - Manage workers (list all/by pool, free, free-all) + diff --git a/doc/cli-docs/harbor-jobservice-workers.md b/doc/cli-docs/harbor-jobservice-workers.md new file mode 100644 index 000000000..40a4cb620 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice-workers.md @@ -0,0 +1,46 @@ +--- +title: harbor jobservice workers +weight: 90 +--- +## harbor jobservice workers + +### Description + +##### Manage workers (list all/by pool, free, free-all) + +### Synopsis + +Manage job service workers using the job service API. + +Use 'list' to view workers from all pools or a specific pool. +Use 'free' and 'free-all' to stop running jobs and release busy workers. + +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool + harbor jobservice worker list + harbor jobservice workers free --job-id + harbor jobservice workers free-all + +### Options + +```sh + -h, --help help for workers +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor jobservice](harbor-jobservice.md) - Manage Harbor job service (admin only) +* [harbor jobservice workers free](harbor-jobservice-workers-free.md) - Free one worker (--job-id required) +* [harbor jobservice workers free-all](harbor-jobservice-workers-free-all.md) - Free all busy workers (job-id=all) +* [harbor jobservice workers list](harbor-jobservice-workers-list.md) - List workers (supports --page and --page-size) + diff --git a/doc/cli-docs/harbor-jobservice.md b/doc/cli-docs/harbor-jobservice.md new file mode 100644 index 000000000..6334ab515 --- /dev/null +++ b/doc/cli-docs/harbor-jobservice.md @@ -0,0 +1,38 @@ +--- +title: harbor jobservice +weight: 55 +--- +## harbor jobservice + +### Description + +##### Manage Harbor job service (admin only) + +### Synopsis + +Manage Harbor job service components including worker pools, job queues, schedules, and job logs. +This requires system admin privileges. + +Use "harbor jobservice [command] --help" for detailed examples and flags per subcommand. + +### Options + +```sh + -h, --help help for jobservice +``` + +### Options inherited from parent commands + +```sh + -c, --config string config file (default is $HOME/.config/harbor-cli/config.yaml) + -o, --output-format string Output format. One of: json|yaml + -v, --verbose verbose output +``` + +### SEE ALSO + +* [harbor](harbor.md) - Official Harbor CLI +* [harbor jobservice jobs](harbor-jobservice-jobs.md) - Manage job logs (view by job ID) +* [harbor jobservice pools](harbor-jobservice-pools.md) - Manage worker pools (list available pools) +* [harbor jobservice workers](harbor-jobservice-workers.md) - Manage workers (list all/by pool, free, free-all) + diff --git a/doc/cli-docs/harbor.md b/doc/cli-docs/harbor.md index fe1916f83..13b4f5420 100644 --- a/doc/cli-docs/harbor.md +++ b/doc/cli-docs/harbor.md @@ -42,6 +42,7 @@ harbor help * [harbor health](harbor-health.md) - Get the health status of Harbor components * [harbor info](harbor-info.md) - Display detailed Harbor system, statistics, and CLI environment information * [harbor instance](harbor-instance.md) - Manage preheat provider instances in Harbor +* [harbor jobservice](harbor-jobservice.md) - Manage Harbor job service (admin only) * [harbor label](harbor-label.md) - Manage labels in Harbor * [harbor ldap](harbor-ldap.md) - Manage ldap users and groups * [harbor login](harbor-login.md) - Log in to Harbor registry diff --git a/doc/man-docs/man1/harbor-jobservice-jobs-log.1 b/doc/man-docs/man1/harbor-jobservice-jobs-log.1 new file mode 100644 index 000000000..6c0a20127 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-jobs-log.1 @@ -0,0 +1,45 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-jobs-log - View a job log (--job-id required) + + +.SH SYNOPSIS +\fBharbor jobservice jobs log [flags]\fP + + +.SH DESCRIPTION +Display the log for a specific job by job ID. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for log + +.PP +\fB--job-id\fP="" + Job ID to fetch log for (required) + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH EXAMPLE +.EX +harbor jobservice jobs log --job-id abc123def456 +.EE + + +.SH SEE ALSO +\fBharbor-jobservice-jobs(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-jobs.1 b/doc/man-docs/man1/harbor-jobservice-jobs.1 new file mode 100644 index 000000000..2886872e4 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-jobs.1 @@ -0,0 +1,35 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-jobs - Manage job logs (view by job ID) + + +.SH SYNOPSIS +\fBharbor jobservice jobs [flags]\fP + + +.SH DESCRIPTION +View logs for specific jobs. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for jobs + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH SEE ALSO +\fBharbor-jobservice(1)\fP, \fBharbor-jobservice-jobs-log(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-pools-list.1 b/doc/man-docs/man1/harbor-jobservice-pools-list.1 new file mode 100644 index 000000000..2c7508e8c --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-pools-list.1 @@ -0,0 +1,42 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-pools-list - List all worker pools + + +.SH SYNOPSIS +\fBharbor jobservice pools list [flags]\fP + + +.SH DESCRIPTION +Display all worker pools with their details. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for list + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH EXAMPLE +.EX +harbor jobservice pools list +harbor jobservice pool list +.EE + + +.SH SEE ALSO +\fBharbor-jobservice-pools(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-pools.1 b/doc/man-docs/man1/harbor-jobservice-pools.1 new file mode 100644 index 000000000..dba187585 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-pools.1 @@ -0,0 +1,43 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-pools - Manage worker pools (list available pools) + + +.SH SYNOPSIS +\fBharbor jobservice pools [flags]\fP + + +.SH DESCRIPTION +List and manage worker pools for the Harbor job service. + +.PP +Use 'list' to view all worker pools. + +.PP +Examples: + harbor jobservice pools list + harbor jobservice pool list + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for pools + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH SEE ALSO +\fBharbor-jobservice(1)\fP, \fBharbor-jobservice-pools-list(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-workers-free-all.1 b/doc/man-docs/man1/harbor-jobservice-workers-free-all.1 new file mode 100644 index 000000000..ce6a9e03e --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-workers-free-all.1 @@ -0,0 +1,41 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-workers-free-all - Free all busy workers (job-id=all) + + +.SH SYNOPSIS +\fBharbor jobservice workers free-all [flags]\fP + + +.SH DESCRIPTION +Stop all running jobs to free all busy workers. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for free-all + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH EXAMPLE +.EX +harbor jobservice workers free-all +.EE + + +.SH SEE ALSO +\fBharbor-jobservice-workers(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-workers-free.1 b/doc/man-docs/man1/harbor-jobservice-workers-free.1 new file mode 100644 index 000000000..9d2ceea91 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-workers-free.1 @@ -0,0 +1,45 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-workers-free - Free one worker (--job-id required) + + +.SH SYNOPSIS +\fBharbor jobservice workers free [flags]\fP + + +.SH DESCRIPTION +Stop a running job by job ID to free its worker. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for free + +.PP +\fB--job-id\fP="" + Running job ID to stop + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH EXAMPLE +.EX +harbor jobservice workers free --job-id abc123 +.EE + + +.SH SEE ALSO +\fBharbor-jobservice-workers(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-workers-list.1 b/doc/man-docs/man1/harbor-jobservice-workers-list.1 new file mode 100644 index 000000000..b8288fc3e --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-workers-list.1 @@ -0,0 +1,75 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-workers-list - List workers (supports --page and --page-size) + + +.SH SYNOPSIS +\fBharbor jobservice workers list [POOL_ID] [flags]\fP + + +.SH DESCRIPTION +List job service workers. + +.PP +Supported listing modes: + - All workers (default): no POOL_ID or --pool all + - Specific pool workers: provide [POOL_ID] or --pool + - Compatibility mode: --pool-all (same as --pool all) + +.PP +Pagination: + - --page selects the 1-based page number + - --page-size controls how many workers are shown per page + +.PP +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool default + harbor jobservice workers list --page 2 --page-size 20 + harbor jobservice workers list default + harbor jobservice worker list 72327cf790564e45b7c89a2d + + +.SH OPTIONS +\fB--all\fP[=false] + List workers from all pools + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for list + +.PP +\fB--page\fP=1 + Page number + +.PP +\fB--page-size\fP=20 + Number of workers per page + +.PP +\fB--pool\fP="" + Worker pool ID (use 'all' for all pools) + +.PP +\fB--pool-all\fP[=false] + List workers from all pools (compatibility alias for --pool all) + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH SEE ALSO +\fBharbor-jobservice-workers(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice-workers.1 b/doc/man-docs/man1/harbor-jobservice-workers.1 new file mode 100644 index 000000000..eb9d39e35 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice-workers.1 @@ -0,0 +1,48 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice-workers - Manage workers (list all/by pool, free, free-all) + + +.SH SYNOPSIS +\fBharbor jobservice workers [flags]\fP + + +.SH DESCRIPTION +Manage job service workers using the job service API. + +.PP +Use 'list' to view workers from all pools or a specific pool. +Use 'free' and 'free-all' to stop running jobs and release busy workers. + +.PP +Examples: + harbor jobservice workers list + harbor jobservice workers list --pool all + harbor jobservice workers list --pool + harbor jobservice worker list + harbor jobservice workers free --job-id + harbor jobservice workers free-all + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for workers + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH SEE ALSO +\fBharbor-jobservice(1)\fP, \fBharbor-jobservice-workers-free(1)\fP, \fBharbor-jobservice-workers-free-all(1)\fP, \fBharbor-jobservice-workers-list(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor-jobservice.1 b/doc/man-docs/man1/harbor-jobservice.1 new file mode 100644 index 000000000..2b66d19b3 --- /dev/null +++ b/doc/man-docs/man1/harbor-jobservice.1 @@ -0,0 +1,39 @@ +.nh +.TH "HARBOR" "1" "Harbor Community" "Harbor User Manuals" + +.SH NAME +harbor-jobservice - Manage Harbor job service (admin only) + + +.SH SYNOPSIS +\fBharbor jobservice [flags]\fP + + +.SH DESCRIPTION +Manage Harbor job service components including worker pools, job queues, schedules, and job logs. +This requires system admin privileges. + +.PP +Use "harbor jobservice [command] --help" for detailed examples and flags per subcommand. + + +.SH OPTIONS +\fB-h\fP, \fB--help\fP[=false] + help for jobservice + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +\fB-c\fP, \fB--config\fP="" + config file (default is $HOME/.config/harbor-cli/config.yaml) + +.PP +\fB-o\fP, \fB--output-format\fP="" + Output format. One of: json|yaml + +.PP +\fB-v\fP, \fB--verbose\fP[=false] + verbose output + + +.SH SEE ALSO +\fBharbor(1)\fP, \fBharbor-jobservice-jobs(1)\fP, \fBharbor-jobservice-pools(1)\fP, \fBharbor-jobservice-workers(1)\fP \ No newline at end of file diff --git a/doc/man-docs/man1/harbor.1 b/doc/man-docs/man1/harbor.1 index cd0884420..f39b7dfbe 100644 --- a/doc/man-docs/man1/harbor.1 +++ b/doc/man-docs/man1/harbor.1 @@ -43,4 +43,4 @@ harbor help .SH SEE ALSO -\fBharbor-artifact(1)\fP, \fBharbor-config(1)\fP, \fBharbor-context(1)\fP, \fBharbor-cve-allowlist(1)\fP, \fBharbor-health(1)\fP, \fBharbor-info(1)\fP, \fBharbor-instance(1)\fP, \fBharbor-label(1)\fP, \fBharbor-ldap(1)\fP, \fBharbor-login(1)\fP, \fBharbor-logs(1)\fP, \fBharbor-password(1)\fP, \fBharbor-project(1)\fP, \fBharbor-quota(1)\fP, \fBharbor-registry(1)\fP, \fBharbor-replication(1)\fP, \fBharbor-repo(1)\fP, \fBharbor-robot(1)\fP, \fBharbor-scan-all(1)\fP, \fBharbor-scanner(1)\fP, \fBharbor-schedule(1)\fP, \fBharbor-tag(1)\fP, \fBharbor-user(1)\fP, \fBharbor-version(1)\fP, \fBharbor-vulnerability(1)\fP, \fBharbor-webhook(1)\fP \ No newline at end of file +\fBharbor-artifact(1)\fP, \fBharbor-config(1)\fP, \fBharbor-context(1)\fP, \fBharbor-cve-allowlist(1)\fP, \fBharbor-health(1)\fP, \fBharbor-info(1)\fP, \fBharbor-instance(1)\fP, \fBharbor-jobservice(1)\fP, \fBharbor-label(1)\fP, \fBharbor-ldap(1)\fP, \fBharbor-login(1)\fP, \fBharbor-logs(1)\fP, \fBharbor-password(1)\fP, \fBharbor-project(1)\fP, \fBharbor-quota(1)\fP, \fBharbor-registry(1)\fP, \fBharbor-replication(1)\fP, \fBharbor-repo(1)\fP, \fBharbor-robot(1)\fP, \fBharbor-scan-all(1)\fP, \fBharbor-scanner(1)\fP, \fBharbor-schedule(1)\fP, \fBharbor-tag(1)\fP, \fBharbor-user(1)\fP, \fBharbor-version(1)\fP, \fBharbor-vulnerability(1)\fP, \fBharbor-webhook(1)\fP \ No newline at end of file diff --git a/pkg/api/jobservice_handler.go b/pkg/api/jobservice_handler.go new file mode 100644 index 000000000..c69eb74fd --- /dev/null +++ b/pkg/api/jobservice_handler.go @@ -0,0 +1,96 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package api + +import ( + "github.com/goharbor/go-client/pkg/sdk/v2.0/client/jobservice" + "github.com/goharbor/harbor-cli/pkg/utils" +) + +// GetWorkerPools retrieves all worker pools +func GetWorkerPools() (*jobservice.GetWorkerPoolsOK, error) { + ctx, client, err := utils.ContextWithClient() + if err != nil { + return nil, err + } + + response, err := client.Jobservice.GetWorkerPools(ctx, &jobservice.GetWorkerPoolsParams{}) + if err != nil { + return nil, err + } + + return response, nil +} + +// GetWorkers retrieves workers for a pool +func GetWorkers(poolID string) (*jobservice.GetWorkersOK, error) { + ctx, client, err := utils.ContextWithClient() + if err != nil { + return nil, err + } + + response, err := client.Jobservice.GetWorkers(ctx, &jobservice.GetWorkersParams{ + PoolID: poolID, + }) + if err != nil { + return nil, err + } + + return response, nil +} + +// StopRunningJob stops a running job by job ID. Use jobID=all to stop all running jobs. +func StopRunningJob(jobID string) error { + ctx, client, err := utils.ContextWithClient() + if err != nil { + return err + } + + _, err = client.Jobservice.StopRunningJob(ctx, &jobservice.StopRunningJobParams{ + JobID: jobID, + }) + return err +} + +// ListJobQueues retrieves all job queues +func ListJobQueues() (*jobservice.ListJobQueuesOK, error) { + ctx, client, err := utils.ContextWithClient() + if err != nil { + return nil, err + } + + response, err := client.Jobservice.ListJobQueues(ctx, &jobservice.ListJobQueuesParams{}) + if err != nil { + return nil, err + } + + return response, nil +} + +// GetJobLog retrieves the log for a job by ID +func GetJobLog(jobID string) (string, error) { + ctx, client, err := utils.ContextWithClient() + if err != nil { + return "", err + } + + response, err := client.Jobservice.ActionGetJobLog(ctx, &jobservice.ActionGetJobLogParams{ + JobID: jobID, + }) + if err != nil { + return "", err + } + + return response.Payload, nil +} diff --git a/pkg/utils/jobservice/errors.go b/pkg/utils/jobservice/errors.go new file mode 100644 index 000000000..a7c222ee0 --- /dev/null +++ b/pkg/utils/jobservice/errors.go @@ -0,0 +1,48 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package jobservice + +import ( + "fmt" + + "github.com/goharbor/harbor-cli/pkg/utils" +) + +func FormatScheduleError(operation string, err error, requiredPermission string) error { + errorCode := utils.ParseHarborErrorCode(err) + + switch errorCode { + case "400": + return fmt.Errorf("%s: invalid request. For schedule status use job_type=all; for queue action use stop|pause|resume", operation) + case "401": + return fmt.Errorf("%s: authentication required. Please run 'harbor login' and try again", operation) + case "403": + if requiredPermission == "authenticated" { + return fmt.Errorf("%s: permission denied. Your account is authenticated but lacks access", operation) + } + return fmt.Errorf("%s: permission denied. This operation requires %s on jobservice-monitor", operation, requiredPermission) + case "404": + return fmt.Errorf("%s: resource not found or not accessible in current context", operation) + case "422": + return fmt.Errorf("%s: request validation failed. Please check request body and action values", operation) + case "500": + return fmt.Errorf("%s: Harbor internal error. Retry and check Harbor server logs", operation) + default: + msg := utils.ParseHarborErrorMsg(err) + if msg == "" { + msg = err.Error() + } + return fmt.Errorf("%s: %s", operation, msg) + } +} diff --git a/pkg/views/jobservice/pools/view.go b/pkg/views/jobservice/pools/view.go new file mode 100644 index 000000000..b7007941d --- /dev/null +++ b/pkg/views/jobservice/pools/view.go @@ -0,0 +1,51 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package pools + +import ( + "fmt" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" +) + +// ListPools displays worker pools in a formatted table. +func ListPools(items []*models.WorkerPool) { + if len(items) == 0 { + fmt.Println("No worker pools found.") + return + } + + fmt.Printf("%-20s %-8s %-30s %-30s %-12s %-30s\n", "POOL_ID", "PID", "START_AT", "HEARTBEAT_AT", "CONCURRENCY", "HOST") + fmt.Printf("%-20s %-8s %-30s %-30s %-12s %-30s\n", "-------", "---", "--------", "------------", "-----------", "----") + + for _, pool := range items { + if pool == nil { + continue + } + + startAt := fmt.Sprintf("%v", pool.StartAt) + heartbeatAt := fmt.Sprintf("%v", pool.HeartbeatAt) + + fmt.Printf("%-20s %-8d %-30s %-30s %-12d %-30s\n", + pool.WorkerPoolID, + pool.Pid, + startAt, + heartbeatAt, + pool.Concurrency, + pool.Host, + ) + } + + fmt.Printf("\nTotal: %d worker pool(s)\n", len(items)) +} diff --git a/pkg/views/jobservice/pools/view_test.go b/pkg/views/jobservice/pools/view_test.go new file mode 100644 index 000000000..1858c07a0 --- /dev/null +++ b/pkg/views/jobservice/pools/view_test.go @@ -0,0 +1,79 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pools + +import ( + "bytes" + "io" + "os" + "strings" + "testing" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" +) + +func captureOutput(t *testing.T, fn func()) string { + t.Helper() + + originalStdout := os.Stdout + reader, writer, err := os.Pipe() + if err != nil { + t.Fatalf("failed to create pipe: %v", err) + } + + os.Stdout = writer + defer func() { os.Stdout = originalStdout }() + + outputCh := make(chan string, 1) + go func() { + var buffer bytes.Buffer + _, _ = io.Copy(&buffer, reader) + outputCh <- buffer.String() + }() + + fn() + + _ = writer.Close() + return <-outputCh +} + +func TestListPoolsEmptyState(t *testing.T) { + output := captureOutput(t, func() { + ListPools(nil) + }) + + if !strings.Contains(output, "No worker pools found.") { + t.Fatalf("expected empty-state message, got %q", output) + } +} + +func TestListPoolsRendersRows(t *testing.T) { + output := captureOutput(t, func() { + ListPools([]*models.WorkerPool{ + { + WorkerPoolID: "pool-1", + Pid: 123, + Concurrency: 5, + Host: "worker-host", + }, + }) + }) + + for _, want := range []string{"POOL_ID", "pool-1", "123", "5", "worker-host", "Total: 1 worker pool(s)"} { + if !strings.Contains(output, want) { + t.Fatalf("expected output to contain %q, got %q", want, output) + } + } +} diff --git a/pkg/views/jobservice/workers/view.go b/pkg/views/jobservice/workers/view.go new file mode 100644 index 000000000..e78018b64 --- /dev/null +++ b/pkg/views/jobservice/workers/view.go @@ -0,0 +1,73 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package workers + +import ( + "fmt" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" +) + +// ListWorkers displays workers in a formatted table with pagination metadata. +func ListWorkers(workers []*models.Worker, page, pageSize, totalCount int64) { + if len(workers) == 0 { + fmt.Println("No workers found.") + return + } + + fmt.Printf("%-40s %-20s %-20s %-40s %-30s %-30s\n", + "ID", "POOL_ID", "JOB_NAME", "JOB_ID", "START_AT", "CHECKIN_AT") + fmt.Printf("%-40s %-20s %-20s %-40s %-30s %-30s\n", + "--", "-------", "--------", "------", "--------", "---------") + + busyCount := 0 + for _, worker := range workers { + id := worker.ID + if id == "" { + id = "-" + } + + poolID := worker.PoolID + if poolID == "" { + poolID = "-" + } + + jobName := worker.JobName + if jobName == "" { + jobName = "-" + } + + jobID := worker.JobID + if jobID == "" { + jobID = "-" + } else { + busyCount++ + } + + startAt := "-" + if worker.StartAt != nil { + startAt = fmt.Sprintf("%v", worker.StartAt) + } + + checkinAt := "-" + if worker.CheckinAt != nil { + checkinAt = fmt.Sprintf("%v", worker.CheckinAt) + } + + fmt.Printf("%-40s %-20s %-20s %-40s %-30s %-30s\n", + id, poolID, jobName, jobID, startAt, checkinAt) + } + + fmt.Printf("\nPage: %d Page Size: %d Returned: %d Total: %d Busy: %d\n", page, pageSize, len(workers), totalCount, busyCount) +} diff --git a/pkg/views/jobservice/workers/view_test.go b/pkg/views/jobservice/workers/view_test.go new file mode 100644 index 000000000..2f6df6928 --- /dev/null +++ b/pkg/views/jobservice/workers/view_test.go @@ -0,0 +1,77 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package workers + +import ( + "bytes" + "io" + "os" + "strings" + "testing" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" +) + +func captureOutput(t *testing.T, fn func()) string { + t.Helper() + + originalStdout := os.Stdout + reader, writer, err := os.Pipe() + if err != nil { + t.Fatalf("failed to create pipe: %v", err) + } + + os.Stdout = writer + defer func() { + os.Stdout = originalStdout + }() + + outputCh := make(chan string, 1) + go func() { + var buffer bytes.Buffer + _, _ = io.Copy(&buffer, reader) + outputCh <- buffer.String() + }() + + fn() + + _ = writer.Close() + return <-outputCh +} + +func TestListWorkersPaginationFooter(t *testing.T) { + output := captureOutput(t, func() { + ListWorkers([]*models.Worker{ + {ID: "worker-1", JobID: "job-1"}, + {ID: "worker-2"}, + }, 2, 20, 5) + }) + + for _, want := range []string{"worker-1", "worker-2", "Page: 2 Page Size: 20 Returned: 2 Total: 5 Busy: 1"} { + if !strings.Contains(output, want) { + t.Fatalf("expected output to contain %q, got %q", want, output) + } + } +} + +func TestListWorkersEmptyState(t *testing.T) { + output := captureOutput(t, func() { + ListWorkers(nil, 1, 20, 0) + }) + + if !strings.Contains(output, "No workers found.") { + t.Fatalf("expected empty-state message, got %q", output) + } +}