diff --git a/go.mod b/go.mod index 3194d373..8007feb7 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,15 @@ go 1.17 require ( github.com/stretchr/testify v1.7.0 - golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 + golang.org/x/net v0.7.0 ) require ( + github.com/antchfx/xmlquery v1.3.17 // indirect + github.com/antchfx/xpath v1.2.4 // indirect github.com/davecgh/go-spew v1.1.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/text v0.7.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 2031a6aa..2118c000 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,49 @@ +github.com/antchfx/xmlquery v1.3.17 h1:d0qWjPp/D+vtRw7ivCwT5ApH/3CkQU8JOeo3245PpTk= +github.com/antchfx/xmlquery v1.3.17/go.mod h1:Afkq4JIeXut75taLSuI31ISJ/zeq+3jG7TunF7noreA= +github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY= +github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 h1:eDd+TJqbgfXruGQ5sJRU7tEtp/58OAx4+Ayjxg4SM+4= golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/jenkins.go b/jenkins.go index 86dc7191..088455c0 100644 --- a/jenkins.go +++ b/jenkins.go @@ -103,7 +103,7 @@ func (j *Jenkins) SafeRestart(ctx context.Context) error { // Create a new Node // Can be JNLPLauncher or SSHLauncher -// Example : jenkins.CreateNode("nodeName", 1, "Description", "/var/lib/jenkins", "jdk8 docker", map[string]string{"method": "JNLPLauncher"}) +// Example : jenkins.CreateNode("nodeName", 1, "Description", "/var/lib/jenkins", "jdk8 docker", map[string]string{"method": "JNLPLauncher", "mode": "exclusive"}) // By Default JNLPLauncher is created // Multiple labels should be separated by blanks func (j *Jenkins) CreateNode(ctx context.Context, name string, numExecutors int, description string, remoteFS string, label string, options ...interface{}) (*Node, error) { @@ -117,13 +117,24 @@ func (j *Jenkins) CreateNode(ctx context.Context, name string, numExecutors int, params["method"] = "JNLPLauncher" } + MODE := "NORMAL" // NOMAL, EXCLUSIVE + if _, ok := params["mode"]; ok { + MODE = strings.ToUpper(params["mode"]) + } + webSocket := "false" + if _, ok := params["websocket"]; ok { + webSocket = params["websocket"] + } + method := params["method"] var launcher map[string]string switch method { case "": fallthrough case "JNLPLauncher": - launcher = map[string]string{"stapler-class": "hudson.slaves.JNLPLauncher"} + launcher = map[string]string{ + "stapler-class": "hudson.slaves.JNLPLauncher", + "webSocket": webSocket} case "SSHLauncher": launcher = map[string]string{ "stapler-class": "hudson.plugins.sshslaves.SSHLauncher", @@ -146,7 +157,6 @@ func (j *Jenkins) CreateNode(ctx context.Context, name string, numExecutors int, node := &Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} NODE_TYPE := "hudson.slaves.DumbSlave$DescriptorImpl" - MODE := "NORMAL" qr := map[string]string{ "name": name, "type": NODE_TYPE, @@ -186,6 +196,11 @@ func (j *Jenkins) DeleteNode(ctx context.Context, name string) (bool, error) { return node.Delete(ctx) } +func (j *Jenkins) GetNodeSecret(ctx context.Context, name string) (string, error) { + node := Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} + return node.GetSecret(ctx) +} + // Create a new folder // This folder can be nested in other parent folders // Example: jenkins.CreateFolder("newFolder", "grandparentFolder", "parentFolder") @@ -505,7 +520,7 @@ func (j *Jenkins) HasPlugin(ctx context.Context, name string) (*Plugin, error) { return p.Contains(name), nil } -//InstallPlugin with given version and name +// InstallPlugin with given version and name func (j *Jenkins) InstallPlugin(ctx context.Context, name string, version string) error { xml := fmt.Sprintf(``, name, version) resp, err := j.Requester.PostXML(ctx, "/pluginManager/installNecessaryPlugins", xml, j.Raw, map[string]string{}) @@ -555,11 +570,13 @@ func (j *Jenkins) GetAllViews(ctx context.Context) ([]*View, error) { // First Parameter - name of the View // Second parameter - Type // Possible Types: -// gojenkins.LIST_VIEW -// gojenkins.NESTED_VIEW -// gojenkins.MY_VIEW -// gojenkins.DASHBOARD_VIEW -// gojenkins.PIPELINE_VIEW +// +// gojenkins.LIST_VIEW +// gojenkins.NESTED_VIEW +// gojenkins.MY_VIEW +// gojenkins.DASHBOARD_VIEW +// gojenkins.PIPELINE_VIEW +// // Example: jenkins.CreateView("newView",gojenkins.LIST_VIEW) func (j *Jenkins) CreateView(ctx context.Context, name string, viewType string) (*View, error) { view := &View{Jenkins: j, Raw: new(ViewResponse), Base: "/view/" + name} diff --git a/jenkins_test.go b/jenkins_test.go index 4f4f1af0..4a7167ad 100644 --- a/jenkins_test.go +++ b/jenkins_test.go @@ -54,6 +54,7 @@ func TestCreateNodes(t *testing.T) { //id2 := "node2_test" id3 := "node3_test" id4 := "node4_test" + id5 := "node5_test" jnlp := map[string]string{"method": "JNLPLauncher"} //ssh := map[string]string{"method": "SSHLauncher"} @@ -69,6 +70,9 @@ func TestCreateNodes(t *testing.T) { assert.Equal(t, id3, node3.GetName()) node4, _ := jenkins.CreateNode(ctx, id4, 1, "Node 4 Description", "/var/lib/jenkins", "jdk7") assert.Equal(t, id4, node4.GetName()) + + node5, _ := jenkins.CreateNode(ctx, id4, 1, "Node 5 Description", "/var/lib/jenkins", "jdk7", map[string]string{"method": "JNLPLauncher", "mode": "exclusive"}) + assert.Equal(t, id5, node5.GetName()) } func TestDeleteNodes(t *testing.T) { diff --git a/node.go b/node.go index 1bea5cc9..11a6e396 100644 --- a/node.go +++ b/node.go @@ -17,6 +17,15 @@ package gojenkins import ( "context" "errors" + "strings" + + "github.com/antchfx/xmlquery" +) + +const ( + XMLComputerSecret = `/argument` + XMLComputerInformation = `/jnlp/application-desc` + XMLComputerInformationFile = "/jenkins-agent.jnlp" ) // Nodes @@ -232,3 +241,22 @@ func (n *Node) GetLogText(ctx context.Context) (string, error) { return log, nil } + +func (n *Node) GetSecret(ctx context.Context) (string, error) { + var data string + _, err := n.Jenkins.Requester.GetXML(ctx, n.Base+XMLComputerInformationFile, &data, nil) + if err != nil { + return "", err + } + dataReader := strings.NewReader(data) + doc, err := xmlquery.Parse(dataReader) + if err != nil { + return "", nil + } + channel := xmlquery.FindOne(doc, XMLComputerInformation) + info := channel.SelectElement(XMLComputerSecret) + if info == nil { + return "", errors.New("Failed to extact secret information") + } + return info.InnerText(), nil +}