Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use withCredentials in declarative Jenkinsfile? #107

Closed
dtitov opened this issue Mar 26, 2019 · 20 comments
Closed

How to use withCredentials in declarative Jenkinsfile? #107

dtitov opened this issue Mar 26, 2019 · 20 comments
Labels

Comments

@dtitov
Copy link

dtitov commented Mar 26, 2019

Hi,

In the example I find:

withCredentials([string(credentialsId: 'mycredentialsid', variable: 'credentialsVariable')]) {
 properties([
  pipelineTriggers([
   [$class: 'GenericTrigger',
    ...
    token: credentialsVariable,
    ...
   ]
  ])
 ])
}

Apparently, this works with scripted Jenkinsfile, but not with declarative Jenkinsfile.

If I try to do something like:

withCredentials([string(credentialsId: 'JENKINS_TOKEN', variable: 'TOKEN')]) {
  triggers {
     genericVariables: [
       ...
     ],
     causeString: 'Triggered by $image',
     token: $TOKEN,
     printContributedVariables: true,
     printPostContent: true
    )
  }
}

it fails with WorkflowScript: 4: Undefined section "withCredentials".

If I put withCredentials inside the triggers section, it fails with WorkflowScript: 5: Triggers definitions cannot have blocks.

So, what's the proper way of using token from credentials with declarative syntax?

@tomasbjerre
Copy link
Contributor

I think that is more of a credentials plugin question. Not something to do with this plugin. https://www.google.com/search?q=jenkins+withcredentials+declarative

@dtitov
Copy link
Author

dtitov commented Mar 26, 2019

@tomasbjerre, I've been googling for it, but found nothing working, so I hoped maybe here people could suggest something.

I also tried this approach:

...
    environment {
      JENKINS_TOKEN = credentials('JENKINS_TOKEN')
    }
    
  triggers {
    GenericTrigger(
     genericVariables: [
       ...
     ],
     causeString: 'Triggered by $image',
     token: env.JENKINS_TOKEN,
     printContributedVariables: true,
     printPostContent: true
    )
  }
...

Syntax was accepted by Jenkins, but then build can't be triggered: "Did not find any jobs with GenericTrigger configured! If you are using a token, you need to pass it like ...trigger/invoke?token=TOKENHERE. If you are not using a token, you need to authenticate like http://user:passsword@jenkins/generic-webhook... "

@tomasbjerre
Copy link
Contributor

You may be experiencing this issue: #100

@dtitov
Copy link
Author

dtitov commented Mar 26, 2019

I rebuilt it twice, didn't help...

Could the problem be that env.JENKINS_TOKEN is assigned to a token only if JENKINS_TOKEN comes from the real environment where the Jenkins itself is launched (server) and not from the environment {...} block of the same file? Because the build is triggered if I use URL without the token at all, so it seems that the token was assigned to an empty value.

@dtitov
Copy link
Author

dtitov commented Mar 27, 2019

Okay, so I figured out a bit. It seems that GenericTrigger is not able to read env vars. I've set the environment variable on the server (Ubuntu): JENKINS_TOKEN=123abc. Then I used it like this:

pipeline {

    agent any

  triggers {
    GenericTrigger(
     genericVariables: [
...
     ],
     token: env.JENKINS_TOKEN
    )
  }

    stages {
        stage('Test') {
            steps {
                script {
                      echo env.JENKINS_TOKEN
                }
            }
        }
    }
}

I manually launched this build two times. Both times JENKINS_TOKEN was printed correctly in the Test stage: 123abc. But when I try to launch the build using a URL <address>/generic-webhook-trigger/invoke?token=123abc, it doesn't work: "Did not find any jobs with GenericTrigger configured! If you are using a token, you need to pass it like ...trigger/invoke?token=TOKENHERE. If you are not using a token, you need to authenticate like http://user:passsword@jenkins/generic-webhook... "

@tomasbjerre
Copy link
Contributor

This is an ordinary pipeline job right? You are not using multibranch pipeline?
Can you repeat this with a curl command? What does that curl command look like?
What version of Jenkins?
What version of the plugin?

You may also find something helpful in these issues: #47 #84 #81

tomasbjerre added a commit that referenced this issue Mar 27, 2019
tomasbjerre added a commit that referenced this issue Mar 27, 2019
@tomasbjerre
Copy link
Contributor

I updated the readme, the "Pipeline" section. You should verify that your configuration has actually been applied.

tomasbjerre added a commit that referenced this issue Mar 27, 2019
@dtitov
Copy link
Author

dtitov commented Mar 28, 2019

Jenkins ver. 2.164.1
Generic Webhook Trigger Plugin ver. 1.52

I'm not quite sure what kind of job is it: I created a project using Blue Ocean and it uses the Jenkins file that is in the repo. Jenkinsfile contains declarative pipeline. Here's the full content of it:

pipeline {
    
    agent any
    
  triggers {
    GenericTrigger(
     genericVariables: [
      [key: 'service', value: '$.service'],
      [key: 'image', value: '$.image']
     ],
     token: env.JENKINS_TOKEN,
     causeString: 'Triggered by $image',
     printContributedVariables: true,
     printPostContent: true
    )
  }

    stages {
        stage('Test') {
            steps {
                script {
                  try {
                      echo env.JENKINS_TOKEN
                      echo service
                      echo image
                  } catch (Exception e) {
                      echo "Using default images"
                  }
                }
            }
        }
    }
}

Give me a second to try curl.

@dtitov
Copy link
Author

dtitov commented Mar 28, 2019

curl -X POST \
>   'https://<jenkins_address>/generic-webhook-trigger/invoke?token=abc123' \
>   -d '{
> "service" : "testService",
> "image" : "org/img:ololo"
> }'
{"status":"ok","data":{"triggerResults":{"ANY":"Did not find any jobs with GenericTrigger configured! If you are using a token, you need to pass it like ...trigger/invoke?token=TOKENHERE. If you are not using a token, you need to authenticate like http://user:passsword@jenkins/generic-webhook... "}}}

It works if I use a static string as a token, like: token: 'abc123'. But not if I point it to an env-var.

@qauff
Copy link

qauff commented May 8, 2019

Yeah I'm not convinced that the GenericTrigger function can access env vars in a declarative Jenkinsfile.

So if we can't access env variables, and we can't use withCredentials in the triggers section. How are we expected to set tokens for triggers in a declarative pipeline? Hardcoded in our Jenkinsfile? 🤢

@tomasbjerre
Copy link
Contributor

The pipeline script is accessing env vars, not the plugin. This issue has nothing to do with this plugin.

Check the readme (pipeline section) on how to verify what config has actually been applied. If you can verify that the token has been persisted in the config but the plugin is still not working as expected: then we may have a bug!

@dtitov
Copy link
Author

dtitov commented May 9, 2019

@tomasbjerre, I don't get it. There are scripted pipelines and there are declarative pipelines, and they work differently, as far as I know. 90% of the readme section that you are referring to, is dedicated to the scripted pipelines, and only the last example is about declarative pipelines. But in this last example, you have token hardcoded. So could you, maybe, provide a working example of how the token can be read from secrets using declarative syntax? I tried lots of different approaches and could never make it work so I've just given up.

@tomasbjerre
Copy link
Contributor

I don't know how to read the token with declarative pipeline. That is a general Jenkins question, has nothing to do with this plugin.

What I would do is one of:

  • Use Job DSL to configure the job. Have Job DSL create a pipeline job. Where the pipeline is the build process and Job DSL configures this triggering.
  • Use a scripted pipeline instead of declarative.

@qauff
Copy link

qauff commented May 9, 2019

@tomasbjerre So you're saying for declarative pipelines, token's must be hardcoded into the Jenkinsfiles because the plugin hasn't been updated to support environment variables for declarative pipelines?

Being able to pass credentials to the plugin in a declarative pipeline seems like a real and necessary use case for a lot of recent Jenkins users. Having to hardcode the token or, as you say, use a scripted pipeline instead, isn't always an option for most people, and kind of defeats the purpose of using a declarative pipeline.

But if that's your stance, perhaps the readme should state very obviously that the plugin doesn't support credentials or environment variables for declarative pipelines. No need for others to waste more time trying to figure that out on their own.

@tomasbjerre
Copy link
Contributor

A way forward here might be to add something like tokenCredentialId to the plugin where the user supplies only the id of the credential.

What is your use case here really? Avoid configuring the token in many places? Because then you should be able to define it with shared libraries.

@qauff
Copy link

qauff commented May 9, 2019

@tomasbjerre I like the tokenCredentialId approach.

The use case is just to not have to hardcode tokens inside code committed to repositories. Especially if it's a public repo. But yeah, also being able to share tokens across multiple pipelines.

@tomasbjerre
Copy link
Contributor

I opened this issue: https://issues.jenkins-ci.org/browse/JENKINS-57390
I suspect it can be solved in declarative pipeline implementation.Perhaps here: https://github.com/jenkinsci/pipeline-model-definition-plugin

@tomasbjerre
Copy link
Contributor

I will probably not work on implementing this in the plugin. The credentials API is really terrible and not easy to use at all. Which is my main reason for not adding it. But pull requests are always welcome!

Quickest solution for you might be to create a shared library where you store the token:
https://jenkins.io/doc/book/pipeline/shared-libraries/

@acepsaepudin
Copy link

Sorry if I commented in this issue that was closed. but you can mix the scripted pipeline and the declarative pipeline for solving your case @dtitov . btw I had similar issue with you ( I want to use environment variable to put my secret token and use it in my declarative pipeline ).

example code:

node {
 withCredentials([string(credentialsId: 'mycredentialsid', variable: 'credentialsVariable')]) {
 properties([
  pipelineTriggers([
   [$class: 'GenericTrigger',
    ...
    token: credentialsVariable,
    ...
   ]
  ])
 ])
}
}

pipeline {
  agent {
    label 'ec2-fleet'
  }
  environment {
    ECR_PROTOCOL = 'https://'
  }

  stages {
    stage('Build Image') {
      steps {
        script {
          docker.build("${ECR_URL}/${REPO_NAME}:${env.DOCKER_TAG}")
        }
      }
    }
  }
}

Ref: https://jenkinsci-users.narkive.com/j2IvGy3N/mixing-declarative-and-scripted-pipeline-syntax-in-one-jenkinsfile

@tomasbjerre
Copy link
Contributor

It now has tokenCredentialId. See readme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants