diff --git a/cmd/command/create.go b/cmd/command/create.go index 8e49356..b33e0c9 100644 --- a/cmd/command/create.go +++ b/cmd/command/create.go @@ -5,7 +5,7 @@ import ( "io/ioutil" jenkins "github.com/jkandasa/jenkinsctl/pkg/jenkins" - cliML "github.com/jkandasa/jenkinsctl/pkg/model/cli" + cliTY "github.com/jkandasa/jenkinsctl/pkg/types/cli" "github.com/jkandasa/jenkinsctl/pkg/utils" stdinUtils "github.com/jkandasa/jenkinsctl/pkg/utils/read_stdin" "github.com/spf13/cobra" @@ -68,7 +68,7 @@ var createResource = &cobra.Command{ } switch resource := resourceInterface.(type) { - case *cliML.KindBuild: + case *cliTY.KindBuild: buildQueueId, err := client.Build(resource.Spec.JobName, resource.Spec.Parameters) if err != nil { fmt.Fprintln(ioStreams.ErrOut, err) @@ -77,7 +77,7 @@ var createResource = &cobra.Command{ fmt.Fprintf(ioStreams.Out, "build created on the job '%s', build queue id:%d\n", resource.Spec.JobName, buildQueueId) return - case *cliML.KindJob: + case *cliTY.KindJob: jobName, err := client.CreateJob(resource.Spec.JobName, resource.Spec.XMLData) if err != nil { fmt.Fprintln(ioStreams.ErrOut, err) diff --git a/cmd/command/get.go b/cmd/command/get.go index cf46782..105a1b9 100644 --- a/cmd/command/get.go +++ b/cmd/command/get.go @@ -7,6 +7,7 @@ import ( jenkins "github.com/jkandasa/jenkinsctl/pkg/jenkins" "github.com/jkandasa/jenkinsctl/pkg/printer" + jenkinsTY "github.com/jkandasa/jenkinsctl/pkg/types/jenkins" "github.com/spf13/cobra" ) @@ -32,8 +33,9 @@ type TableData struct { } var ( - limit int - watch bool + limit int + watch bool + queueID int64 ) func init() { @@ -44,7 +46,7 @@ func init() { getCmd.PersistentFlags().IntVar(&limit, "limit", 5, "limit the number of entries to display") getConsole.PersistentFlags().BoolVarP(&watch, "watch", "w", false, "watch build console logs") - + getBuilds.PersistentFlags().Int64Var(&queueID, "queue-id", 0, "filter by queue id") } var getCmd = &cobra.Command{ @@ -77,7 +79,10 @@ var getBuilds = &cobra.Command{ jenkinsctl get builds --limit 2 --output json --pretty # get a particular build details with build number - jenkinsctl get build 61`, + jenkinsctl get build 61 + + # get a particular build details with queue id + jenkinsctl get build --queue-id 1234`, Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { client := jenkins.NewClient(CONFIG, &ioStreams) @@ -85,17 +90,34 @@ var getBuilds = &cobra.Command{ return } - if len(args) > 0 { - buildNumber, err := strconv.Atoi(args[0]) - if err != nil { - fmt.Fprintln(ioStreams.ErrOut, "build should be an integer number", err) - return - } - build, err := client.GetBuild(CONFIG.JobContext, buildNumber, false) - if err != nil { - fmt.Fprintln(ioStreams.ErrOut, "error on getting build details", err) - return + if len(args) > 0 || queueID > 0 { + var build *jenkinsTY.BuildResponse + + if len(args) > 0 { + buildNumber, err := strconv.Atoi(args[0]) + if err != nil { + fmt.Fprintln(ioStreams.ErrOut, "build should be an integer number", err) + return + } + receivedBuild, err := client.GetBuild(CONFIG.JobContext, buildNumber, false) + if err != nil { + fmt.Fprintln(ioStreams.ErrOut, "error on getting build details", err) + return + } + build = receivedBuild + } else { + receivedBuild, err := client.GetBuildByQueueID(CONFIG.JobContext, queueID, limit) + if err != nil { + fmt.Fprintln(ioStreams.ErrOut, "error on getting build details", err) + return + } + if receivedBuild == nil { + fmt.Fprintln(ioStreams.ErrOut, "there is no build available with this queue id in the job:", CONFIG.JobContext) + return + } + build = receivedBuild } + if outputFormat != printer.OutputConsole { printer.Print(ioStreams.Out, nil, build, false, outputFormat, pretty) return @@ -104,6 +126,7 @@ var getBuilds = &cobra.Command{ headers := []string{"key", "value"} rows := make([]interface{}, 0) rows = append(rows, TableData{Key: "URL", Value: build.URL}) + rows = append(rows, TableData{Key: "Queue ID", Value: fmt.Sprintf("%d", build.QueueID)}) rows = append(rows, TableData{Key: "Build Number", Value: fmt.Sprintf("%d", build.Number)}) rows = append(rows, TableData{Key: "Triggered By", Value: build.TriggeredBy}) rows = append(rows, TableData{Key: "Result", Value: build.Result}) @@ -152,7 +175,7 @@ var getBuilds = &cobra.Command{ return } - headers := []string{"number", "triggered by", "result", "is running", "duration", "timestamp", "revision"} + headers := []string{"number", "queue id", "triggered by", "result", "is running", "duration", "timestamp", "revision"} data := make([]interface{}, 0) for _, build := range builds { data = append(data, build) diff --git a/cmd/command/root.go b/cmd/command/root.go index 572bb76..e2e01ea 100644 --- a/cmd/command/root.go +++ b/cmd/command/root.go @@ -6,9 +6,9 @@ import ( "os" "path/filepath" - model "github.com/jkandasa/jenkinsctl/pkg/model" - "github.com/jkandasa/jenkinsctl/pkg/model/config" "github.com/jkandasa/jenkinsctl/pkg/printer" + types "github.com/jkandasa/jenkinsctl/pkg/types" + "github.com/jkandasa/jenkinsctl/pkg/types/config" homedir "github.com/mitchellh/go-homedir" "gopkg.in/yaml.v2" @@ -25,7 +25,7 @@ const ( var ( cfgFile string CONFIG *config.Config // keep jenkins server details - ioStreams model.IOStreams // read and write to this stream + ioStreams types.IOStreams // read and write to this stream jobContext string hideHeader bool @@ -60,7 +60,7 @@ func init() { rootCmd.PersistentFlags().BoolVar(&pretty, "pretty", false, "JSON pretty print") } -func Execute(streams model.IOStreams) { +func Execute(streams types.IOStreams) { ioStreams = streams if err := rootCmd.Execute(); err != nil { fmt.Fprintln(ioStreams.ErrOut, err) diff --git a/cmd/main.go b/cmd/main.go index 32fefc8..e7db961 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,10 +2,10 @@ package main import ( "github.com/jkandasa/jenkinsctl/cmd/command" - "github.com/jkandasa/jenkinsctl/pkg/model" + types "github.com/jkandasa/jenkinsctl/pkg/types" ) func main() { - streams := model.NewStdStreams() + streams := types.NewStdStreams() command.Execute(streams) } diff --git a/go.mod b/go.mod index ceca2a6..ed574d8 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/bndr/gojenkins v1.1.0 github.com/fatih/structs v1.1.0 github.com/mitchellh/go-homedir v1.1.0 + github.com/mxschmitt/playwright-go v0.1400.0 github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 @@ -13,7 +14,9 @@ require ( ) require ( + github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/magiconair/properties v1.8.5 // indirect @@ -29,4 +32,5 @@ require ( golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) diff --git a/go.sum b/go.sum index 38dca86..a4028ef 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWH github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -194,7 +196,10 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= @@ -284,6 +289,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxschmitt/playwright-go v0.1400.0 h1:HL8dbxcVEobE+pNjASeYGJJRmd4+9gyu/51XO7d3qF0= +github.com/mxschmitt/playwright-go v0.1400.0/go.mod h1:kUvZFgMneRGknVLtC2DKQ42lhZiCmWzxgBdGwjC0vkw= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -772,6 +779,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/jenkins/client.go b/pkg/jenkins/client.go index 367c11f..a6dd914 100644 --- a/pkg/jenkins/client.go +++ b/pkg/jenkins/client.go @@ -13,20 +13,20 @@ import ( "time" "github.com/bndr/gojenkins" - "github.com/jkandasa/jenkinsctl/pkg/model" - "github.com/jkandasa/jenkinsctl/pkg/model/config" - jenkinsML "github.com/jkandasa/jenkinsctl/pkg/model/jenkins" + types "github.com/jkandasa/jenkinsctl/pkg/types" + "github.com/jkandasa/jenkinsctl/pkg/types/config" + jenkinsTY "github.com/jkandasa/jenkinsctl/pkg/types/jenkins" ) // Client type type Client struct { - ioStreams *model.IOStreams + ioStreams *types.IOStreams api *gojenkins.Jenkins ctx context.Context } // NewClient function to get client instance -func NewClient(cfg *config.Config, streams *model.IOStreams) *Client { +func NewClient(cfg *config.Config, streams *types.IOStreams) *Client { httpClient := http.DefaultClient httpClient.Transport = http.DefaultTransport httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: cfg.InsecureSkipTLSVerify} @@ -114,15 +114,16 @@ func (jc *Client) Status() (*gojenkins.ExecutorResponse, error) { } // GetBuild returns build details of a job -func (jc *Client) GetBuild(jobName string, buildNumber int, withConsole bool) (*jenkinsML.BuildResponse, error) { +func (jc *Client) GetBuild(jobName string, buildNumber int, withConsole bool) (*jenkinsTY.BuildResponse, error) { buildRaw, err := jc.api.GetBuild(jc.ctx, jobName, int64(buildNumber)) if err != nil { return nil, err } - build := jenkinsML.BuildResponse{ - URL: buildRaw.GetUrl(), - Number: buildRaw.GetBuildNumber(), + build := jenkinsTY.BuildResponse{ + QueueID: buildRaw.Raw.QueueID, + URL: buildRaw.GetUrl(), + Number: buildRaw.GetBuildNumber(), } // update triggered by if causes, err := buildRaw.GetCauses(jc.ctx); err == nil { @@ -140,9 +141,9 @@ func (jc *Client) GetBuild(jobName string, buildNumber int, withConsole bool) (* } } - build.Parameters = make([]jenkinsML.Parameter, 0) + build.Parameters = make([]jenkinsTY.Parameter, 0) for _, p := range buildRaw.GetParameters() { - build.Parameters = append(build.Parameters, jenkinsML.Parameter{Name: p.Name, Value: fmt.Sprintf("%v", p.Value)}) + build.Parameters = append(build.Parameters, jenkinsTY.Parameter{Name: p.Name, Value: fmt.Sprintf("%v", p.Value)}) } if injectedEnvVars, err := buildRaw.GetInjectedEnvVars(jc.ctx); err != nil { @@ -168,7 +169,7 @@ func (jc *Client) GetBuild(jobName string, buildNumber int, withConsole bool) (* } for _, artifact := range buildRaw.GetArtifacts() { - build.Artifacts = append(build.Artifacts, jenkinsML.Artifact{FileName: artifact.FileName, Path: artifact.Path}) + build.Artifacts = append(build.Artifacts, jenkinsTY.Artifact{FileName: artifact.FileName, Path: artifact.Path}) } if withConsole { @@ -184,9 +185,23 @@ func (jc *Client) GetBuild(jobName string, buildNumber int, withConsole bool) (* return &build, nil } +// returns build details based on queue id +func (jc *Client) GetBuildByQueueID(jobName string, queueID int64, limit int) (*jenkinsTY.BuildResponse, error) { + jobs, err := jc.ListBuilds(jobName, limit, false) + if err != nil { + return nil, err + } + for _, job := range jobs { + if job.QueueID == queueID { + return &job, nil + } + } + return nil, nil +} + // ListBuilds details -func (jc *Client) ListBuilds(jobName string, limit int, withConsole bool) ([]jenkinsML.BuildResponse, error) { - builds := make([]jenkinsML.BuildResponse, 0) +func (jc *Client) ListBuilds(jobName string, limit int, withConsole bool) ([]jenkinsTY.BuildResponse, error) { + builds := make([]jenkinsTY.BuildResponse, 0) buildIds, err := jc.api.GetAllBuildIds(jc.ctx, jobName) if err != nil { return nil, err @@ -280,6 +295,7 @@ func (jc *Client) DownloadArtifacts(jobName string, buildNumber int, toDirectory } // Build a job with parameters +// returns queue id func (jc *Client) Build(name string, parameters map[string]string) (int64, error) { return jc.api.BuildJob(jc.ctx, name, parameters) } diff --git a/pkg/model/cli/config.go b/pkg/types/cli/config.go similarity index 100% rename from pkg/model/cli/config.go rename to pkg/types/cli/config.go diff --git a/pkg/model/config/model.go b/pkg/types/config/types.go similarity index 100% rename from pkg/model/config/model.go rename to pkg/types/config/types.go diff --git a/pkg/model/iostreams.go b/pkg/types/iostreams.go similarity index 100% rename from pkg/model/iostreams.go rename to pkg/types/iostreams.go diff --git a/pkg/model/jenkins/model.go b/pkg/types/jenkins/types.go similarity index 95% rename from pkg/model/jenkins/model.go rename to pkg/types/jenkins/types.go index 8b0a44e..f4ac124 100644 --- a/pkg/model/jenkins/model.go +++ b/pkg/types/jenkins/types.go @@ -20,8 +20,9 @@ type Artifact struct { // BuildResponse struct type BuildResponse struct { - URL string `json:"url" yaml:"url" structs:"url"` + QueueID int64 `json:"queueId" yaml:"queue_id" structs:"queue id"` Number int64 `json:"number" yaml:"number" structs:"number"` + URL string `json:"url" yaml:"url" structs:"url"` TriggeredBy string `json:"triggeredBy" yaml:"triggered_by" structs:"triggered by"` Parameters []Parameter `json:"parameters" yaml:"parameters" structs:"parameters"` InjectedEnvVars map[string]string `json:"injectedEnvVars" yaml:"injected_env_vars" structs:"injected env var"` diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index ade830d..464d848 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "github.com/fatih/structs" - cliML "github.com/jkandasa/jenkinsctl/pkg/model/cli" + cliYTY "github.com/jkandasa/jenkinsctl/pkg/types/cli" "gopkg.in/yaml.v2" ) @@ -24,7 +24,7 @@ func FileToStruct(filename string, out interface{}) error { } func GetResource(bytes []byte) (interface{}, error) { - kindData := &cliML.Kind{} + kindData := &cliYTY.Kind{} err := yaml.Unmarshal(bytes, kindData) if err != nil { return nil, err @@ -33,11 +33,11 @@ func GetResource(bytes []byte) (interface{}, error) { var resource interface{} switch kindData.Kind { - case cliML.KindTypeBuild: - resource = &cliML.KindBuild{} + case cliYTY.KindTypeBuild: + resource = &cliYTY.KindBuild{} - case cliML.KindTypeJob: - resource = &cliML.KindJob{} + case cliYTY.KindTypeJob: + resource = &cliYTY.KindJob{} default: return nil, fmt.Errorf("unknown kind:%s", kindData.Kind)