Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-72716

credentialsId shows different in `Jenkins.instance.nodes` and `Jenkins.instance.computers`

    • Icon: Bug Bug
    • Resolution: Not A Defect
    • Icon: Critical Critical
    • core, ssh-slaves-plugin

      I've been updated credential ID in the SSH Agent ( hudson.plugins.sshslaves.SSHLauncher ), when I using script to check the credentialsId status, find out the jenkins.model.Jenkins.instance.nodes works as I saw in configure page, however, the jenkins.model.Jenkins.instance.computers still shows the legacy ( previous ) credentialsId .

       

      groovy script for list nodes credentials:

      computer.getNode():

      Jenkins.instance.computers.findAll { computer ->
        ! jenkins.model.Jenkins.MasterComputer.isInstance(computer) &&
        computer?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher
      }.each { computer ->
        println computer.node.launcher?.credentialsId?.toString()     // shows updated, same with configuration page
        println computer.launcher?.credentialsId?.toString()          // issue: shows the legacy credentialsId
      }
      

       
      node.toComputer():

      Jenkins.instance.nodes.findAll { node ->
        ! jenkins.model.Jenkins.MasterComputer.isInstance(node) &&
        node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher
      }.each { node ->
        println node.launcher?.credentialsId?.toString() + ' : ' + node.getClass()
        println node.toComputer().launcher?.credentialsId?.toString() + ' : ' + node.toComputer().getClass()
      }
      

      screenshot:

      BTW, here is the groovy script to update nodes credential:

      import jenkins.model.Jenkins
      import hudson.plugins.sshslaves.SSHLauncher
      import hudson.slaves.ComputerLauncher
      
      String newCredId = 'GIT_SSH_CREDENTIAL'
      
      Jenkins.instance.nodes.findAll { node ->
        ! jenkins.model.Jenkins.MasterComputer.isInstance(node) &&
        node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher
      }.each { node ->
        ComputerLauncher launcher = node.launcher
        SSHLauncher newLauncher = new SSHLauncher( launcher.host, launcher.port, newCredId )
        node.setLauncher( newLauncher )
        node.save()
        println ">> ${node.name} DONE <<"
      }
      

          [JENKINS-72716] credentialsId shows different in `Jenkins.instance.nodes` and `Jenkins.instance.computers`

          Does it get back in sync if you call SlaveComputer.setNode(Node), which would then do launcher = grabLauncher(node);?  The setNode method is protected but perhaps you can call it via Jenkins.setNodes(List).

          Kalle Niemitalo added a comment - Does it get back in sync if you call SlaveComputer.setNode(Node) , which would then do launcher = grabLauncher(node); ?  The setNode method is protected but perhaps you can call it via Jenkins.setNodes(List) .

          Markus Winter added a comment -

          Probably the change on computer only gets active after disconnecting the agent and reconnecting it. Changing the configuration of an agent will not automatically reconnect it. Some changes are immediately visible, like labels, agent root, availability and usage, but the connection is not closed, so there will be no change to the currently active Launcher in the Computer class. It would probably be not a good idea to automatically disconnect when the launcher changes as this could kill running builds.

          Markus Winter added a comment - Probably the change on computer only gets active after disconnecting the agent and reconnecting it. Changing the configuration of an agent will not automatically reconnect it. Some changes are immediately visible, like labels, agent root, availability and usage, but the connection is not closed, so there will be no change to the currently active Launcher in the Computer class. It would probably be not a good idea to automatically disconnect when the launcher changes as this could kill running builds.

          Marslo Jiao added a comment - - edited

          Hi kon ,

           

          I suppose you're right. It somehow cannot sync configure automatically in Jenkins runtime. Even if the configure has been updated ( agent.xml ), but in Jenkins itself, it stills read the legacy credential.

          So, what I did to re-sync manually by

          1. open the configure page
          2. click save button

          and it updated the Jenkins runtime settings.

           

          So, does jenkins.model.Jenkins.setNode( node.name ) will automatic "sync" the configure in Jenkins runtime ?

          My current solution is to delete node -> create node via:

          import hudson.slaves.*
          import hudson.model.Node.Mode
          import jenkins.model.Jenkins
          import hudson.plugins.sshslaves.SSHLauncher
          
          String newCredId = 'GIT_SSH_CREDENTIAL'
          
          Jenkins.instance.nodes.findAll { node ->
            ! jenkins.model.Jenkins.MasterComputer.isInstance(node) &&
            node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher
          }.each { node ->
            println ">> ${node.name} update <<"
            ComputerLauncher launcher = node.launcher
            SSHLauncher newLauncher = new SSHLauncher( launcher.host,
                                                       launcher.port,
                                                       newCredId,
                                                       launcher.jvmOptions,
                                                       launcher.javaPath,
                                                       launcher.prefixStartSlaveCmd,
                                                       launcher.suffixStartSlaveCmd,
                                                       launcher.launchTimeoutSeconds,
                                                       launcher.maxNumRetries,
                                                       launcher.retryWaitTime,
                                                       launcher.sshHostKeyVerificationStrategy
                                                     )
            DumbSlave agent = new DumbSlave( node.name, node.remoteFS, newLauncher )
            agent.nodeDescription   = node.nodeDescription
            agent.numExecutors      = node.numExecutors
            agent.labelString       = node.labelString
            agent.mode              = node.mode
            agent.retentionStrategy = node.retentionStrategy
            node.computer.doDoDelete()
            Thread.sleep( 5*1000 )
            Jenkins.instance.addNode( agent )
          }
          
          "DONE"
          

          UPDATE

          I've tried the :

          Jenkins.instance.nodes.collect { Jenkins.updateNode(it) }
          

          got issue:

          groovy.lang.MissingMethodException: No signature of method: static jenkins.model.Jenkins.updateNode() is applicable for argument types: (hudson.slaves.DumbSlave)
          

          And

          Jenkins.instance.nodes.collect{ it.load() }
          

          got issue:

          groovy.lang.MissingMethodException: No signature of method: hudson.slaves.DumbSlave.load() is applicable for argument types: () values: []
          

          Marslo Jiao added a comment - - edited Hi kon ,   I suppose you're right. It somehow cannot sync configure automatically in Jenkins runtime. Even if the configure has been updated ( agent.xml ), but in Jenkins itself, it stills read the legacy credential. So, what I did to re-sync manually by open the configure page click save button and it updated the Jenkins runtime settings.   So, does jenkins.model.Jenkins.setNode( node.name ) will automatic "sync" the configure in Jenkins runtime ? My current solution is to delete node -> create node via: import hudson.slaves.* import hudson.model.Node.Mode import jenkins.model.Jenkins import hudson.plugins.sshslaves.SSHLauncher String newCredId = 'GIT_SSH_CREDENTIAL' Jenkins.instance.nodes.findAll { node -> ! jenkins.model.Jenkins.MasterComputer.isInstance(node) && node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher }.each { node -> println ">> ${node.name} update <<" ComputerLauncher launcher = node.launcher SSHLauncher newLauncher = new SSHLauncher( launcher.host, launcher.port, newCredId, launcher.jvmOptions, launcher.javaPath, launcher.prefixStartSlaveCmd, launcher.suffixStartSlaveCmd, launcher.launchTimeoutSeconds, launcher.maxNumRetries, launcher.retryWaitTime, launcher.sshHostKeyVerificationStrategy ) DumbSlave agent = new DumbSlave( node.name, node.remoteFS, newLauncher ) agent.nodeDescription = node.nodeDescription agent.numExecutors = node.numExecutors agent.labelString = node.labelString agent.mode = node.mode agent.retentionStrategy = node.retentionStrategy node.computer.doDoDelete() Thread .sleep( 5*1000 ) Jenkins.instance.addNode( agent ) } "DONE" UPDATE I've tried the : Jenkins.instance.nodes.collect { Jenkins.updateNode(it) } got issue: groovy.lang.MissingMethodException: No signature of method: static jenkins.model.Jenkins.updateNode() is applicable for argument types: (hudson.slaves.DumbSlave) And Jenkins.instance.nodes.collect{ it.load() } got issue: groovy.lang.MissingMethodException: No signature of method: hudson.slaves.DumbSlave.load() is applicable for argument types: () values: []

          Marslo Jiao added a comment - - edited

          Hi mawinter69,

           

          I do run the offline -> disconnect -> connect -> online to check status, it won't help. sample script:

          import jenkins.model.Jenkins
          import hudson.plugins.sshslaves.SSHLauncher
          import hudson.slaves.ComputerLauncher
          
          String newCredId = 'NEW_CREDENTIAL'
          
          Jenkins.instance.nodes.findAll { node ->
            ! jenkins.model.Jenkins.MasterComputer.isInstance(node) &&
            node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher
          }.each { node ->
            ComputerLauncher launcher = node.launcher
            SSHLauncher newLauncher = new SSHLauncher( launcher.host, launcher.port, newCredId )
            node.setLauncher( newLauncher )
            node.save()
            println ">> ${node.name} DONE <<"
          
            // disconnect agent
            if ( node.computer.isOnline() && node.computer.countBusy() == 0 ) {
              println ">> ${node.name} disconnect <<"
              String message = 'disconnect due to credential update'
              node.computer.setTemporarilyOffline( true, new hudson.slaves.OfflineCause.ByCLI(message) )
              node.computer.disconnect( new hudson.slaves.OfflineCause.UserCause(User.current(), message ) )
              node.computer.doChangeOfflineCause( message )
              println '\t.. computer.getOfflineCause: ' + node.computer.getOfflineCause();
            }
          
            // connect agent
            Thread.sleep( 5*1000 )
            if ( node.computer.isOffline() ) {
              println ">> ${node.name} connect <<"
              node.getComputer().connect( true )
              node.computer.setTemporarilyOffline( false, null )
            }
          }
          
          "DONE"
          

           

          Marslo Jiao added a comment - - edited Hi mawinter69 ,   I do run the offline -> disconnect -> connect -> online to check status, it won't help. sample script: import jenkins.model.Jenkins import hudson.plugins.sshslaves.SSHLauncher import hudson.slaves.ComputerLauncher String newCredId = 'NEW_CREDENTIAL' Jenkins.instance.nodes.findAll { node -> ! jenkins.model.Jenkins.MasterComputer.isInstance(node) && node?.launcher instanceof hudson.plugins.sshslaves.SSHLauncher }.each { node -> ComputerLauncher launcher = node.launcher SSHLauncher newLauncher = new SSHLauncher( launcher.host, launcher.port, newCredId ) node.setLauncher( newLauncher ) node.save() println ">> ${node.name} DONE <<" // disconnect agent if ( node.computer.isOnline() && node.computer.countBusy() == 0 ) { println ">> ${node.name} disconnect <<" String message = 'disconnect due to credential update' node.computer.setTemporarilyOffline( true , new hudson.slaves.OfflineCause.ByCLI(message) ) node.computer.disconnect( new hudson.slaves.OfflineCause.UserCause(User.current(), message ) ) node.computer.doChangeOfflineCause( message ) println '\t.. computer.getOfflineCause: ' + node.computer.getOfflineCause(); } // connect agent Thread .sleep( 5*1000 ) if ( node.computer.isOffline() ) { println ">> ${node.name} connect <<" node.getComputer().connect( true ) node.computer.setTemporarilyOffline( false , null ) } } "DONE"  

          Markus Winter added a comment -

          When you modify an agent via the UI you get a new Node object. This in the end calls computer.setNode which updates the launcher

          As groovy doesn't care about protected or private methods you could just call this method I think

          ...
          node.setLauncher(newLauncher)
          node.save()
          node.computer.setNode(node)

          Markus Winter added a comment - When you modify an agent via the UI you get a new Node object. This in the end calls computer.setNode which updates the launcher As groovy doesn't care about protected or private methods you could just call this method I think ... node.setLauncher(newLauncher) node.save() node.computer.setNode(node)

          Marslo Jiao added a comment -

          thanks mawinter69 , you're my saver !!

          Marslo Jiao added a comment - thanks mawinter69 , you're my saver !!

          Markus Winter added a comment -

          Closing as not a defect

          Markus Winter added a comment - Closing as not a defect

            Unassigned Unassigned
            marslo Marslo Jiao
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: