diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f0c36..3c16a1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.1.0] +## Added +New pipeline resource parameter : `-r pipeline`. + It is possible to operator: list, delete and delete-all on pipelines + Pipelines require to give a project ID Number with parameter `-i ` + Additional added pipeline ID parameter for delete operation: `--pipelineId ` +## Changed +Round the LastActivity/Finished value from listing projects and pipelines ## [1.0.2] ## Added diff --git a/README.md b/README.md index 89b28db..5d03084 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,22 @@ The CLI is able to list, remove and archive a set of resources. | Action | Resource | Query filter applicable | Age filter applicable | Status filter applicable | Example | |---|---|---|---|---|---| |list|user|YES|-|-| List user with name admin:
`gitlab-sanity-cli -o list -r user -q admin`| -|list|project|YES|YES|-| List internal projects older two years:
`gitlab-sanity-cli -o list -r project -a 24 -p internal`| +|list|project|YES|YES|-| List internal projects older two years:
`gitlab-sanity-cli -o list -r project -a 24 -p public`| |list|runner|YES|-|YES| List docker based runner:
`gitlab-sanity-cli -o list -r runner -q docker`| |list|groupRunner|YES|-|YES| List online kubernetes based runner:
`gitlab-sanity-cli -o list -r groupRunner -q kubernetes -s online` | +|list|pipeline|-|YES|-| List all Pipelines from specific project id:
`gitlab-sanity-cli -o list -r pipeline -i 1337 -a 0` | |delete|user|-|-|-| Delete is not capable on users | |delete|project|-|-|-| Remove project with ID 123:
`gitlab-sanity-cli -o delete -r project -i 123`| |delete|runner|-|-|-| Remove runner with ID 123:
`gitlab-sanity-cli -o delete -r runner -i 123`| |delete|groupRunner|-|-|-| Remove runner with ID 123:
`gitlab-sanity-cli -o delete -r groupRunner -i 123`| +|delete|pipeline|-|-|-| Remove Pipeline with ID 123 from project:
`gitlab-sanity-cli -o delete -r pipeline -i 1337 --pipelineId 123` | |delete-all|user|-|-|-|Delete-All is not capable on users | |delete-all|project|YES|YES|-| Remove all projects with name testing:
`gitlab-sanity-cli -o delete-all -r project -a 0 -q testing`

Remove all projects older than five years:
`gitlab-sanity-cli -o delete-all -r project -a 60`| |delete-all|runner|YES|-|YES| Remove all offline runner:
`gitlab-sanity-cli -o delete-all -r runner -s offline`| |delete-all|groupRunner|YES|-|YES| Remove all groupRunner (offline and online):
`gitlab-sanity-cli -o delete-all -r groupRunner` | +|delete-all|pipeline|-|YES|-| Remove all Pipelines from project:
`gitlab-sanity-cli -o delete-all -r pipeline -i 1337` | |archive|project|-|-|-|Archive project with ID 123:
`gitlab-sanity-cli -o archive -r project -i 123`| |archive-all|project|YES|YES|-|Archive project with name testing:
`gitlab-sanity-cli -o archive-all -r project -q testing -a 0 -p private`| - ## How to run ### Requirements diff --git a/VERSION b/VERSION index e6d5cb8..1cc5f65 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.2 \ No newline at end of file +1.1.0 \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index beeb1bd..05dc14c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,7 +18,7 @@ import ( ) const ( - version = "1.0.2" + version = "1.1.0" hoursPerMonth = 30 * 24 newLine = "\r\n" ) @@ -63,6 +63,18 @@ func validateParameters(config *gitlabsanitycli.Config) error { if len(config.ProjectType) < 1 { config.ProjectType = "internal" } + switch config.ProjectType { + case "private", "internal", "public": + default: + return errors.New("Project type not valid. Please choose between: public, internal, private") + } + + if config.Resource == gitlabsanitycli.Pipeline { + if config.ResourceId <= 0 { + return errors.New("Need project ID to get Pipelines: `-i `") + } + } + return nil } @@ -73,11 +85,13 @@ func newHandlers(git *gitlab.Client, config gitlabsanitycli.Config) map[string]m userHandler := gitlabsanitycli.UserHandler{Git: git, Printer: printer, Config: &config} runnerHandler := gitlabsanitycli.RunnerHandler{Git: git, Printer: printer, Config: &config} groupRunnerHandler := gitlabsanitycli.GroupRunnerHandler{Git: git, Printer: printer, Config: &config} + pipelineHandler := gitlabsanitycli.PipelineHandler{Git: git, Printer: printer, Config: &config} handlers[gitlabsanitycli.List] = make(map[string]func()) handlers[gitlabsanitycli.List][gitlabsanitycli.Project] = projectHandler.List handlers[gitlabsanitycli.List][gitlabsanitycli.User] = userHandler.List handlers[gitlabsanitycli.List][gitlabsanitycli.Runner] = runnerHandler.List handlers[gitlabsanitycli.List][gitlabsanitycli.GroupRunner] = groupRunnerHandler.List + handlers[gitlabsanitycli.List][gitlabsanitycli.Pipeline] = pipelineHandler.List handlers[gitlabsanitycli.Archive] = make(map[string]func()) handlers[gitlabsanitycli.Archive][gitlabsanitycli.Project] = projectHandler.Archive handlers[gitlabsanitycli.ArchiveAll] = make(map[string]func()) @@ -86,10 +100,12 @@ func newHandlers(git *gitlab.Client, config gitlabsanitycli.Config) map[string]m handlers[gitlabsanitycli.Delete][gitlabsanitycli.Project] = projectHandler.Delete handlers[gitlabsanitycli.Delete][gitlabsanitycli.Runner] = runnerHandler.Delete handlers[gitlabsanitycli.Delete][gitlabsanitycli.GroupRunner] = groupRunnerHandler.Delete + handlers[gitlabsanitycli.Delete][gitlabsanitycli.Pipeline] = pipelineHandler.Delete handlers[gitlabsanitycli.DeleteAll] = make(map[string]func()) handlers[gitlabsanitycli.DeleteAll][gitlabsanitycli.Project] = projectHandler.DeleteAll handlers[gitlabsanitycli.DeleteAll][gitlabsanitycli.Runner] = runnerHandler.DeleteAll handlers[gitlabsanitycli.DeleteAll][gitlabsanitycli.GroupRunner] = groupRunnerHandler.DeleteAll + handlers[gitlabsanitycli.DeleteAll][gitlabsanitycli.Pipeline] = pipelineHandler.DeleteAll return handlers } diff --git a/config/stdout.tpl b/config/stdout.tpl index 30c6b6c..a104e36 100644 --- a/config/stdout.tpl +++ b/config/stdout.tpl @@ -9,4 +9,6 @@ Runner ID: {{ .ID}} ID: {{ .ID }}, Name: {{ .Name }} {{ else if eq .Req "project" -}} ID: {{ .ID }}, Name: {{ .Name }}, Last Activity: {{ .LastActivity }} h +{{ else if eq .Req "pipeline" -}} +ID: {{ .ID }}, ProjectID: {{ .ProjectID }}, Status: {{ .Status }}, Finished: {{ .LastActivity }} h ago, Url: {{ .URL }} {{ end }} \ No newline at end of file diff --git a/go.mod b/go.mod index 39fa3d9..05487f5 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ replace github.com/iteratec/gitlab-sanity-cli/pkg => ./pkg require ( github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 - github.com/xanzy/go-gitlab v0.50.1 + github.com/xanzy/go-gitlab v0.59.0 ) diff --git a/go.sum b/go.sum index 43058e3..bd0c19b 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= @@ -19,8 +21,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 h1:txplJASvd6b/hrE0s/Ixfpp2cuwH9IO9oZBAN9iYa4A= github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2/go.mod h1:DGCIhurYgnLz8J9ga1fMV/fbLDyUvTyrWXVWUIyJon4= -github.com/xanzy/go-gitlab v0.50.1 h1:eH1G0/ZV1j81rhGrtbcePjbM5Ern7mPA4Xjt+yE+2PQ= -github.com/xanzy/go-gitlab v0.50.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= +github.com/xanzy/go-gitlab v0.59.0 h1:fAr6rT/YIdfmBavYgI42+Op7yAAex2Y4xOfvbjN9hxQ= +github.com/xanzy/go-gitlab v0.59.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -39,6 +41,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/config.go b/pkg/config.go index a4e369e..c224da0 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -18,6 +18,7 @@ const ( Runner = "runner" GroupRunner = "groupRunner" User = "user" + Pipeline = "pipeline" // GitLab API OK Response Code gitlabReponseCodeOk = 204 @@ -34,8 +35,9 @@ type Config struct { Insecure bool `goptions:"--insecure, description='Skip certificate Verfication for Gitlab API URL, (bool)'"` Token string `goptions:"-t, --token, description='Gitlab API access token, can also be set via env \"GITLAB_TOKEN\" or file \".token\"'"` Operation string `goptions:"-o, --operation, description='Operation to start, (list, archive, archive-all, delete, delete-all)', obligatory"` - Resource string `goptions:"-r, --resource, description='Resource to interact with, (project, runner, groupRunner, user)', obligatory"` + Resource string `goptions:"-r, --resource, description='Resource to interact with, (project, runner, groupRunner, user, pipeline)', obligatory"` ResourceId int `goptions:"-i, --identifier, description='Resource ID to interact with, (int)'"` + PipelineId int `goptions:"--pipelineId, description='Pipeline ID to interact with, (int)'"` ProjectType string `goptions:"-p, --project-type, description='Type of project (internal, private, public), (default: internal), (string)'"` Age int `goptions:"-a, --age, description='Filter by last activity in months (not available for runner), (int)'"` Query string `goptions:"-q, --query, description='Search by name, (string)'"` diff --git a/pkg/content.go b/pkg/content.go index 10aaabd..54c2db7 100644 --- a/pkg/content.go +++ b/pkg/content.go @@ -14,4 +14,6 @@ type content struct { GroupName string GroupID int LastActivity float64 + URL string + ProjectID int } diff --git a/pkg/pipeline.go b/pkg/pipeline.go new file mode 100644 index 0000000..2030a9b --- /dev/null +++ b/pkg/pipeline.go @@ -0,0 +1,110 @@ +package gitlabsanitycli + +import ( + "log" + "math" + "sync" + "time" + + "github.com/xanzy/go-gitlab" +) + +// PipelineHandler implements AbstractHandler +// implementation for interacting with Gitlab Project Pipelines +type PipelineHandler struct { + Git *gitlab.Client + Printer Printer + Config *Config + ListOptions gitlab.ListProjectPipelinesOptions +} + +func (p PipelineHandler) Pipe(wg *sync.WaitGroup, wc chan<- content, rc <-chan int) { + defer wg.Done() + now := time.Now() + for id := range rc { + pipeline, _, err := p.Git.Pipelines.GetPipeline(p.Config.ResourceId, id) + if err != nil { + log.Fatal(err) + } + if pipeline.FinishedAt == nil { + log.Printf("Unable to fetch data for Pipeline ID: %v, URL: %v", pipeline.ID, pipeline.WebURL) + } else { + // Age filter + if now.Sub(*pipeline.FinishedAt).Hours() > float64(p.Config.Age) { + wc <- content{ + Req: Pipeline, + ID: pipeline.ID, + Status: pipeline.Status, + URL: pipeline.WebURL, + ProjectID: pipeline.ProjectID, + LastActivity: math.Round(now.Sub(*pipeline.FinishedAt).Hours()), + } + } + } + } +} + +func (p PipelineHandler) ApiCallFunc(page int) ([]int, *gitlab.Response, error) { + p.ListOptions.Page = page + + values, resp, err := p.Git.Pipelines.ListProjectPipelines(p.Config.ResourceId, &p.ListOptions) + + var xIds []int + for _, value := range values { + xIds = append(xIds, value.ID) + } + return xIds, resp, err +} + +func (p PipelineHandler) Controller(channelHandlerFunc ChannelHandlerFunc) { + p.ListOptions = gitlab.ListProjectPipelinesOptions{ + Sort: gitlab.String("desc"), + ListOptions: gitlab.ListOptions{PerPage: PageSize}, + } + + _, resp, err := p.Git.Pipelines.ListProjectPipelines(p.Config.ResourceId, &p.ListOptions) + + if err != nil { + log.Fatal(err) + } + + genericHandler(p, channelHandlerFunc, resp.TotalPages) + +} + +func (p PipelineHandler) deletePipeline(pipelineId int, dryRun bool) { + project, _, err0 := p.Git.Projects.GetProject(p.Config.ResourceId, nil) + if err0 != nil { + log.Fatal(err0) + } + log.Printf("Deleting Pipeline %v in Project: %v\n", pipelineId, project.Name) + if !dryRun { + _, err1 := p.Git.Pipelines.DeletePipeline(project.ID, pipelineId) + if err1 != nil { + log.Fatal(err1) + } + log.Printf("Pipeline %v deleted \n", pipelineId) + } else { + log.Printf("[DryRun] Pipeline %v was not deleted\n", pipelineId) + } +} + +func (p PipelineHandler) deleteAllPipeline(Pipeline <-chan content) { + go func() { + for pipeline := range Pipeline { + p.deletePipeline(pipeline.ID, p.Config.DryRun) + } + }() +} + +func (p PipelineHandler) List() { + Run(p, p.Printer.Print) +} + +func (p PipelineHandler) Delete() { + p.deletePipeline(p.Config.ResourceId, p.Config.DryRun) +} + +func (p PipelineHandler) DeleteAll() { + Run(p, p.deleteAllPipeline) +} diff --git a/pkg/project.go b/pkg/project.go index 0df0444..509d327 100644 --- a/pkg/project.go +++ b/pkg/project.go @@ -2,6 +2,7 @@ package gitlabsanitycli import ( "log" + "math" "sync" "time" @@ -31,7 +32,7 @@ func (p ProjectHandler) Pipe(wg *sync.WaitGroup, wc chan<- content, rc <-chan in Req: Project, ID: project.ID, Name: project.Name, - LastActivity: now.Sub(*project.LastActivityAt).Hours(), + LastActivity: math.Round(now.Sub(*project.LastActivityAt).Hours()), } } }