Coder Social home page Coder Social logo

geerlingguy / ansible-role-jenkins Goto Github PK

View Code? Open in Web Editor NEW
818.0 44.0 745.0 245 KB

Ansible Role - Jenkins CI

Home Page: https://galaxy.ansible.com/geerlingguy/jenkins/

License: MIT License

Groovy 100.00%
ansible role jenkins ci continuous-integration

ansible-role-jenkins's Introduction

Ansible Role: Jenkins CI

CI

Installs Jenkins CI on RHEL/CentOS and Debian/Ubuntu servers.

Requirements

Requires curl to be installed on the server. Also, newer versions of Jenkins require Java 8+ (see the test playbooks inside the molecule/default directory for an example of how to use newer versions of Java for your OS).

Role Variables

Available variables are listed below, along with default values (see defaults/main.yml):

jenkins_package_state: present

The state of the jenkins package install. By default this role installs Jenkins but will not upgrade Jenkins (when using package-based installs). If you want to always update to the latest version, change this to latest.

jenkins_hostname: localhost

The system hostname; usually localhost works fine. This will be used during setup to communicate with the running Jenkins instance via HTTP requests.

jenkins_home: /var/lib/jenkins

The Jenkins home directory which, amongst others, is being used for storing artifacts, workspaces and plugins. This variable allows you to override the default /var/lib/jenkins location.

jenkins_http_port: 8080

The HTTP port for Jenkins' web interface.

jenkins_admin_username: admin
jenkins_admin_password: admin

Default admin account credentials which will be created the first time Jenkins is installed.

jenkins_admin_password_file: ""

Default admin password file which will be created the first time Jenkins is installed as /var/lib/jenkins/secrets/initialAdminPassword

jenkins_jar_location: /opt/jenkins-cli.jar

The location at which the jenkins-cli.jar jarfile will be kept. This is used for communicating with Jenkins via the CLI.

jenkins_plugins:
  - blueocean
  - name: influxdb
    version: "1.12.1"

Jenkins plugins to be installed automatically during provisioning. Defaults to empty list ([]). Items can use name or dictionary with name and version keys to pin specific version of a plugin.

jenkins_plugins_install_dependencies: true

Whether Jenkins plugins to be installed should also install any plugin dependencies.

jenkins_plugins_state: present

Use latest to ensure all plugins are running the most up-to-date version. For any plugin that has a specific version set in jenkins_plugins list, state present will be used instead of jenkins_plugins_state value.

jenkins_plugin_updates_expiration: 86400

Number of seconds after which a new copy of the update-center.json file is downloaded. Set it to 0 if no cache file should be used.

jenkins_updates_url: "https://updates.jenkins.io"

The URL to use for Jenkins plugin updates and update-center information.

jenkins_plugin_timeout: 30

The server connection timeout, in seconds, when installing Jenkins plugins.

jenkins_version: "2.346"
jenkins_pkg_url: "http://www.example.com"

(Optional) Then Jenkins version can be pinned to any version available on http://pkg.jenkins-ci.org/debian/ (Debian/Ubuntu) or http://pkg.jenkins-ci.org/redhat/ (RHEL/CentOS). If the Jenkins version you need is not available in the default package URLs, you can override the URL with your own; set jenkins_pkg_url (Note: the role depends on the same naming convention that http://pkg.jenkins-ci.org/ uses).

jenkins_url_prefix: ""

Used for setting a URL prefix for your Jenkins installation. The option is added as --prefix={{ jenkins_url_prefix }} to the Jenkins initialization java invocation, so you can access the installation at a path like http://www.example.com{{ jenkins_url_prefix }}. Make sure you start the prefix with a / (e.g. /jenkins).

jenkins_connection_delay: 5
jenkins_connection_retries: 60

Amount of time and number of times to wait when connecting to Jenkins after initial startup, to verify that Jenkins is running. Total time to wait = delay * retries, so by default this role will wait up to 300 seconds before timing out.

jenkins_prefer_lts: false

By default, this role will install the latest version of Jenkins using the official repositories according to the platform. You can install the current LTS version instead by setting this to false.

The default repositories (listed below) can be overridden as well.

# For RedHat/CentOS:
jenkins_repo_url: https://pkg.jenkins.io/redhat{{ '-stable' if (jenkins_prefer_lts | bool) else '' }}/jenkins.repo
jenkins_repo_key_url: https://pkg.jenkins.io/redhat{{ '-stable' if (jenkins_prefer_lts | bool) else '' }}/jenkins.io.key

# For Debian/Ubuntu:
jenkins_repo_url: deb https://pkg.jenkins.io/debian{{ '-stable' if (jenkins_prefer_lts | bool) else '' }} binary/
jenkins_repo_key_url: https://pkg.jenkins.io/debian{{ '-stable' if (jenkins_prefer_lts | bool) else '' }}/jenkins.io.key

It is also possible to prevent the repo file from being added by setting jenkins_repo_url: ''. This is useful if, for example, you sign your own packages or run internal package management (e.g. Spacewalk).

jenkins_options: ""

Extra options (e.g. setting the HTTP keep alive timeout) to pass to Jenkins on startup via JENKINS_OPTS in the systemd override.conf file can be configured using the var jenkins_options. By default, no options are specified.

jenkins_java_options: "-Djenkins.install.runSetupWizard=false"

Extra Java options for the Jenkins launch command configured via JENKINS_JAVA_OPTS in the systemd override.conf file can be set with the var jenkins_java_options. For example, if you want to configure the timezone Jenkins uses, add -Dorg.apache.commons.jelly.tags.fmt.timeZone=America/New_York. By default, the option to disable the Jenkins 2.0 setup wizard is added.

jenkins_init_changes:
  - option: "JENKINS_OPTS"
    value: "{{ jenkins_options }}"
  - option: "JAVA_OPTS"
    value: "{{ jenkins_java_options }}"
  - option: "JENKINS_HOME"
    value: "{{ jenkins_home }}"
  - option: "JENKINS_PREFIX"
    value: "{{ jenkins_url_prefix }}"
  - option: "JENKINS_PORT"
    value: "{{ jenkins_http_port }}"

Changes made to the Jenkins systemd override.conf file; the default set of changes set the configured URL prefix, Jenkins home directory, Jenkins port and adds the configured Jenkins and Java options for Jenkins' startup. You can add other option/value pairs if you need to set other options for the Jenkins systemd override.conf file.

jenkins_proxy_host: ""
jenkins_proxy_port: ""
jenkins_proxy_noproxy:
  - "127.0.0.1"
  - "localhost"

If you are running Jenkins behind a proxy server, configure these options appropriately. Otherwise Jenkins will be configured with a direct Internet connection.

Dependencies

None.

Example Playbook

- hosts: jenkins
  become: true
  
  vars:
    jenkins_hostname: jenkins.example.com
    java_packages:
      - openjdk-8-jdk

  roles:
    - role: geerlingguy.java
    - role: geerlingguy.jenkins

Note that java_packages may need different versions depending on your distro (e.g. openjdk-11-jdk for Debian 10, or java-1.8.0-openjdk for RHEL 7 or 8).

License

MIT (Expat) / BSD

Author Information

This role was created in 2014 by Jeff Geerling, author of Ansible for DevOps.

ansible-role-jenkins's People

Contributors

aleskiontherun avatar andreasscherbaum avatar bngsudheer avatar dpursehouse avatar gaieges avatar geerlingguy avatar gone-skiing avatar itzikb avatar krumstein avatar leifmadsen avatar loicortola avatar marcosdiez avatar martinnowak avatar mches avatar mdelapenya avatar mtevruchte-trifork avatar neglect-yp avatar nickbroon avatar rbnrye avatar rbramwell avatar ricardclau avatar robbinscp avatar rsanzante avatar samchoraria avatar sebastianludwig avatar sf-nh avatar solita-timo-mihaljov avatar stephenlauck avatar tkanemoto avatar tylerturk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ansible-role-jenkins's Issues

Provide option to set runSetupWizard=false in JENKINS_JAVA_OPTIONS

Without this option -Djenkins.install.runSetupWizard=false, on a new install of Jenkins 2.0 it will require some manual intervention in the GUI in order to complete deployment so that the proper Jenkins homepage will be displayed.

I use this role to test deployments of Jenkins along for new plugin development, changes to Jenkins Job Builder config, and groovy scripts for interacting with internal Jenkins settings. The Setup Wizard interferes with proper testing of this.

I propose adding this option to JENKINS_JAVA_OPTIONS in the service config file (or adding an ansible var that allows arbitrary strings to be added to this). This file is at /etc/sysconfig/jenkins in a RedHat install, but I don't know how that will vary by OS/distribution.

selinux python bindings and java installation issue

Greetings,

during the test on centos/6 vagrant box that runs on VirtualBox on Windows7, I got such error at

TASK [ansible-role-jenkins : Ensure URL prefix is present in Jenkins config.]

fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}

Probably one more task shall be added to setup-RedHat.yml:

- name: install selinux bindings 
  yum: name=libselinux-python state=present

Or dependencies task must be updated:

- name: Ensure dependencies are installed.
  yum: pkg={{item}} state=installed
  with_items:
    - curl
    - libselinux-python

Also I had an issue with java at

TASK [ansible-role-java-master : include] **************************************
included: /vagrant/ansible-role-java-master/tasks/setup-RedHat.yml for localhost

TASK [ansible-role-java-master : Ensure Java is installed.]

failed: [localhost] (item=[u'java-1.7.0-openjdk']) => {"changed": true, "failed": true, "item": ["java-1.7.0-openjdk"], "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror, security\n"]}

When I switched to root user the error disappeared, so I guess I have to add become: yes to some tasks or to the entire play, right?

I am not sure it can be considered as an issue, maybe more like a suggestion for improvement.

configuring jobs

how would one go about configuring jobs. Say we have all the xml configuration from a previously installed jenkis.

Allow configuration of jenkins proxy host and proxy port

When using the role to install jenkins behind proxy, jenkins server is OK, but jenkin plugins installation failed. To execute jenkins plugins install cli, /etc/default/jenkins should set JAVA_ARGS with -Dhttp.proxyHost=web-proxy.cce.hp.com -Dhttp.proxyPort=8080.
I suppose use_proxy flag should be set. If use_proxy is true, settings.xml would execute task for insert JAVA_ARGS+=" -Dhttp.proxyHost={{ proxyHost }} -Dhttp.proxyPort={{ proxyPort }}" .

Empty jenkins_url_prefix breaking jenkins updates?

Hi

I am facing a weird issue when running updates on an existing Jenkins 2.x server
The update process is complaining about the last line in /etc/default/jenkins because $PREFIX is empty (which is the role's default value)

JENKINS_ARGS+=" --prefix="

Is there any workaround for that?

Unable to install git related plugins

Hi,
I am unable to install git related plugins with the code given by you.
While running it is showing blue color as if already installed but when I see in installed plugin tab the plugins are not there in the list.
I am installing git and git-client plugin.
Do I need to don any kind of update?

Thanks in advance :)

Unable to setup jenkins on a newly provisioned server until dns propogates

I was just running this on a dynamic DO inventory on a brand new server and it was hanging on the task to make jenkins start at boot. This is because jenkins was being curled against what will ultimately be its final url, but it doesn't work because DNS isn't propagated. I was able to fake it by setting up my host file before setting up jenkins. Is there a better way? Here is my task for faking my host file.

  - name: "Build hosts file"
    lineinfile:
      dest: /etc/hosts
      regexp: ci\.example\.com
      line: "127.0.0.1 ci.example.com"
      state: present

Allow installing a given jenkins version

Greetings from downstream (solita/ansible-role-solita.jenkins)!

As part of the effort of making a Jenkins installation completely configurable and reproducible with Ansible, we are looking into allowing installation of a specific Jenkins version.

The most likely way to go is to use the packages on http://mirrors.jenkins-ci.org/ for specific versions.

This is a heads-up: are you interested in reviewing (and merging) such a feature into ansible-role-jenkins?

And do you have any thoughts / concerns at this point?

ansible-lint complains about curl and get_url module

ansible-lint site.yml
[ANSIBLE0006] curl used in place of get_url module
/var/lib/jenkins/jobs/infrastructure/workspace/ansible/roles/geerlingguy.jenkins/tasks/main.yml:0
Task/Handler: Wait for Jenkins to start up before proceeding.

[ANSIBLE0006] curl used in place of get_url module
/var/lib/jenkins/jobs/infrastructure/workspace/ansible/roles/geerlingguy.jenkins/tasks/plugins.yml:0
Task/Handler: Update Jenkins plugin data.

Release of jenkins 2.0 seems to break this role

With the release of Jenkins 2.0, the deployment procedure has changed somewhat. On first installing and starting, Jenkins 2.0 wants to lead you through a one time set up of an admin user. In order to do so, you must enter a one time password written to /

This password should be pasted into the web UI in order to allow configuration of an admin user.

Until such a user is set up, Jenkins will deny any attempts to use jenkins-cli.jar. This role relies on that CLI interface to install plugins (and I would guess for other features too).

I think that being able to specify which version of Jenkins you wish to install would workaround this problem, as in this issue: #38

Stopping web services on default port before install.

Any chance we can disable services on the default port (8080) before installing, or switching to the desired port "{{ jenkins_http_port }}" before installation?

I know it's small, but this particular problem has been giving me headaches a while now.

My particular case is where either nginx or apache are started when trying to install jenkins.
There is a work around, but it required running the provisioning process twice in addition to manually changing the service port between running the script.

I would like to say, please, pretty please can we have this - I'm sure there's a demand for it.

Console output (for evidence):

TASK [geerlingguy.jenkins : Install our specific version of Jenkins.] **********
fatal: [20160623143214.loc.gbuild.net]: FAILED! => {"changed": false, "failed": true, "msg": "dpkg --force-confdef --force-confold -i /tmp/jenkins.deb failed", "stderr": "dirname: missing operand\nTry `dirname --help' for more information.\ndirname: missing operand\nTry `dirname --help' for more information.\nchown: missing operand\nTry `chown --help' for more information.\nInvalid --pidfile argument: '' (Must be an absolute file path)\nusage: daemon [options] [--] [cmd arg...]\noptions:\n\n      -h, --help              - Print a help message then exit\n      -V, --version           - Print a version message then exit\n      -v, --verbose[=level]   - Set the verbosity level\n      -d, --debug[=level]     - Set the debugging level\n\n      -C, --config=path       - Specify the configuration file\n      -N, --noconfig          - Bypass the system configuration file\n      -n, --name=name         - Guarantee a single named instance\n      -X, --command=cmd       - Specify the client command as an option\n      -P, --pidfiles=/dir     - Override standard pidfile location\n      -F, --pidfile=/path     - Override standard pidfile name and location\n\n      -u, --user=user[:group] - Run the client as user[:group]\n      -R, --chroot=path       - Run the client with path as root\n      -D, --chdir=path        - Run the client in directory path\n      -m, --umask=umask       - Run the client with the given umask\n      -e, --env=\"var=val\"     - Set a client environment variable\n      -i, --inherit           - Inherit environment variables\n      -U, --unsafe            - Allow execution of unsafe executable\n      -S, --safe              - Deny execution of unsafe executable\n      -c, --core              - Allow core file generation\n\n      -r, --respawn           - Respawn the client when it terminates\n      -a, --acceptable=#      - Minimum acceptable client duration (seconds)\n      -A, --attempts=#        - Respawn # times on error before delay\n      -L, --delay=#           - Delay between spawn attempt bursts (seconds)\n      -M, --limit=#           - Maximum number of spawn attempt bursts\n          --idiot             - Idiot mode (trust root with the above)\n\n      -f, --foreground        - Run the client in the foreground\n      -p, --pty[=noecho]      - Allocate a pseudo terminal for the client\n\n      -l, --errlog=spec       - Send daemon's error output to syslog or file\n      -b, --dbglog=spec       - Send daemon's debug output to syslog or file\n      -o, --output=spec       - Send client's output to syslog or file\n      -O, --stdout=spec       - Send client's stdout to syslog or file\n      -E, --stderr=spec       - Send client's stderr to syslog or file\n\n          --running           - Check if a named daemon is running\n          --restart           - Restart a named daemon client\n          --stop              - Terminate a named daemon process\ninvoke-rc.d: initscript jenkins, action \"start\" failed.\ndpkg: error processing jenkins (--install):\n subprocess installed post-installation script returned error exit status 7\nErrors were encountered while processing:\n jenkins\n", "stdout": "Selecting previously unselected package jenkins.\n(Reading database ... 189309 files and directories currently installed.)\nUnpacking jenkins (from /tmp/jenkins.deb) ...\nSetting up jenkins (2.9) ...\nInstalling new version of config file /etc/init.d/jenkins ...\n * Starting Jenkins Continuous Integration Server jenkins\nThe selected http port (8080) seems to be in use by another program \nPlease select another port to use for jenkins\n   ...fail!\nProcessing triggers for ureadahead ...\n", "stdout_lines": ["Selecting previously unselected package jenkins.", "(Reading database ... 189309 files and directories currently installed.)", "Unpacking jenkins (from /tmp/jenkins.deb) ...", "Setting up jenkins (2.9) ...", "Installing new version of config file /etc/init.d/jenkins ...", " * Starting Jenkins Continuous Integration Server jenkins", "The selected http port (8080) seems to be in use by another program ", "Please select another port to use for jenkins", "   ...fail!", "Processing triggers for ureadahead ..."]}

I've created PR #88 is association with this task.

HTTPS support

I'm working in a private fork to configure SSL for jenkins. For this there are two major changes that have to be done, and different ways to do them; but I'd like your feedback on them since I would want to merge the changes back when I'm done.

/etc/sysconfig/jenkins variable updates
I'm thinking of a template file with default values (jenkins_config_* named variables)

Configurable protocol and port
During the plugin installation http is used and the 8080 port. Might have two variables jenkins_protocol and jenkins_port; maybe reuse the jenkins_config_https_port (if set) from the configuration setup instead of having two variables do the same thing?

Unmet dependencies problem: daemon

jenkins : Depends: daemon but it is not installable

TASK [roles/geerlingguy.jenkins : Validate Jenkins is installed and register package name.] ***
fatal: [PAL-ansibleclient01]: FAILED! => {"cache_update_time": 0, "cache_updated": false, "changed": false, "failed": true, "msg": "'/usr/bin/apt-get -y -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" install 'jenkins'' failed: E: Unable to correct problems, you have held broken packages.\n", "stderr": "E: Unable to correct problems, you have held broken packages.\n", "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nSome packages could not be installed. This may mean that you have\nrequested an impossible situation or if you are using the unstable\ndistribution that some required packages have not yet been created\nor been moved out of Incoming.\nThe following information may help to resolve the situation:\n\nThe following packages have unmet dependencies:\n jenkins : Depends: daemon but it is not installable\n", "stdout_lines": ["Reading package lists...", "Building dependency tree...", "Reading state information...", "Some packages could not be installed. This may mean that you have", "requested an impossible situation or if you are using the unstable", "distribution that some required packages have not yet been created", "or been moved out of Incoming.", "The following information may help to resolve the situation:", "", "The following packages have unmet dependencies:", " jenkins : Depends: daemon but it is not installable"]}

Intermittent package install failure on Google Compute

Hi there,

Seeing an intermittent issue on this role where installing packages fails due to this issue:

https://issues.jenkins-ci.org/browse/JENKINS-35197

It appears the issue is with Jenkins 2.0, and per the readme the package install may have issues with Jenkins 2, however I do have success installing at least half the time.

I do however have a large amount of plugins I am installing and it appears the more times it has to install a plugin the more likely it is to happen.

I have updated my role config as follows:

jenkins_admin_username: admin
jenkins_admin_password: ""
jenkins_admin_password_file: /var/lib/jenkins/secrets/initialAdminPassword

jenkins_init_changes:
  - option: "JAVA_ARGS"
    value: "-Dhudson.diyChunking=false"

jenkins_plugins:
  # Jenkins Recommended
  - subversion
  - cloudbees-folder
  - antisamy-markup-formatter
  - build-timeout
  - credentials-binding
  - timestamper
  - ws-cleanup
  - ant
  - gradle
  - workflow-scm-step
  - workflow-step-api
  - workflow-cps
  - workflow-support
  - workflow-basic-steps
  - pipeline-input-step
  - pipeline-milestone-step
  - pipeline-build-step
  - pipeline-stage-view
  - workflow-multibranch
  - workflow-durable-task-step
  - workflow-api
  - workflow-cps-global-lib
  - workflow-step-api
  - workflow-job
  - workflow-aggregator
  - github-organization-folder
  - ssh-slaves
  - matrix-auth
  - pam-auth
  - matrix-project
  - email-ext
  - mailer
  - credentials
  - git-client
  - external-monitor-job
  - mapdb-api
  - plain-credentials
  - script-security
  - structs
  - token-macro
  # Blue Ocean
  - metadata
  - favorite
  - blueocean-web
  - blueocean-rest-impl
  - blueocean-commons
  - blueocean-dashboard
  - blueocean-personalization
  - blueocean-jwt
  - blueocean-config
  - blueocean-rest
  - pipeline-model-definition
  - blueocean-pipeline-api-impl
  # Oraganization
  - junit
  - github-oauth
  - simple-theme-plugin
  - git-parameter
  - scm-sync-configuration
  - ssh-credentials
  - ansicolor
  - slack
  - github
  - ghprb
  - build-pipeline-plugin
  - conditional-buildstep
  - parameterized-trigger
  - ssh
  - extensible-choice-parameter
  - multiple-scms
  - view-job-filters
  - embeddable-build-status
  - preSCMbuildstep
  - postbuild-task
  - configurationslicing
  - docker-build-step
  - docker-custom-build-environment
  - jira
  - greenballs
  - nested-view
  - saferestart
  - job-dsl

The error output from Ansible is this:

==> googlecompute: starting sftp subsystem
    googlecompute: failed: [default] (item=subversion) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "subversion", "--username", "admin", "--password-file", "/var/lib/jenkins/secrets/initialAdminPassword"], "delta": "0:00:09.593071", "end": "2016-10-09 20:55:19.003563", "failed": true, "item": "subversion", "rc": 255, "start": "2016-10-09 20:55:09.410492", "stderr": "Oct 09, 2016 8:55:18 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run\nSEVERE: I/O error in channel Chunked connection to http://localhost:8080/cli\njava.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:73)\nCaused by: java.io.EOFException\n\tat java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)\n\tat java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)\n\tat java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)\n\tat java.io.ObjectInputStream.<init>(ObjectInputStream.java:301)\n\tat hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:48)\n\tat hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:59)\n\nhudson.remoting.RequestAbortedException: java.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.Request.abort(Request.java:303)\n\tat hudson.remoting.Channel.terminate(Channel.java:863)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:92)\n\tat ......remote call to Chunked connection to http://localhost:8080/cli(Native Method)\n\tat hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1433)\n\tat hudson.remoting.Request.call(Request.java:172)\n\tat hudson.remoting.Channel.call(Channel.java:796)\n\tat hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:252)\n\tat com.sun.proxy.$Proxy3.main(Unknown Source)\n\tat hudson.cli.CLI.execute(CLI.java:332)\n\tat hudson.cli.CLI._main(CLI.java:503)\n\tat hudson.cli.CLI.main(CLI.java:384)\nCaused by: java.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:73)\nCaused by: java.io.EOFException\n\tat java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)\n\tat java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)\n\tat java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)\n\tat java.io.ObjectInputStream.<init>(ObjectInputStream.java:301)\n\tat hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:48)\n\tat hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:59)", "stdout": "Installing subversion from update center", "stdout_lines": ["Installing subversion from update center"], "warnings": []}

As a solution to this problem is it possible to run a check that the error output is not in the results of running the command on each iteration of the loop?

I tried doing so but the task just skipped when it was in place. I tried adding this:

- name: Install Jenkins plugins using password-file.
  command: >
    java -jar {{ jenkins_jar_location }} -s http://{{ jenkins_hostname }}:{{ jenkins_http_port }}{{ jenkins_url_prefix | default('') }}/
    install-plugin {{ item }}
    --username {{ jenkins_admin_username }}
    --password-file {{ jenkins_admin_password_file }}
    creates={{ jenkins_home }}/plugins/{{ item }}.jpi
  with_items: "{{ jenkins_plugins }}"
  when: adminpasswordfile.stat.exists == True
  register: plugin_install_result
  until: "'Unexpected termination of the channel' not in plugin_install_result"
  retries: 5
  delay: 10
  notify: restart jenkins

I am also finding a couple other smaller issues:

Intermittently the jenkins_admin_password_file appears to be missing on some runs, and then the "Install Jenkins plugins using password-file." task skips.

Sometimes the password task runs even though the jenkins_admin_password field is an empty string.

CHANGELOG file?

I was just looking for the changes that have been introduced in 2.3.0. Was not too hard to look at the diffs, but a CHANGELOG.md file would have been nice, actually.

Btw: props for making all the tasks idempotent, I really like to see that on the 2nd run:

PLAY RECAP *********************************************************************
default                    : ok=25   changed=0    unreachable=0    failed=0

Jenkins plugin install failures

Sometimes jenkins plugin installation would be failed. It seems that jenkins has random issue.

I use retries to handle this issue.

- name: Install Jenkins plugins
  command: >
    java -jar {{ jenkins_jar_location }} -s http://{{ jenkins_hostname }}:{{ jenkins_http_port }}{{ jenkins_url_prefix | default('') }}/ install-plugin {{ item }}
    creates=/var/lib/jenkins/plugins/{{ item }}.jpi
  with_items: "{{ custom_jenkins_plugins }}"
  register: res
  until: res.stderr == false
  retries: "{{ jenkins_connection_retries }}"
  delay: "{{ jenkins_connection_delay }}"
  notify: restart jenkins

The running log is:

TASK [jenkins : Install Jenkins plugins] ***************************************
ok: [target1] => (item=ssh)
FAILED - RETRYING: TASK: jenkins : Install Jenkins plugins (2 retries left).
ok: [target1] => (item=git)

idempotence - Unstable Travis

After some Pull Request in error with the idempotence check, I try with my own Travis Here for PR #56 .
And I try again and again, and with the exact same code, the results are not always the same. The only constant thing is that the idempotence fails, sometimes randomly.

I tried on my laptop with a script of mine extracted from your .travis.yml (See below). And everything is always OK. That is why I think that there is an issue with the Travis part.

Maybe you can add a "Test role 2" like below in order to explain the lake of idempotence. That could be useful to be able to found out the "changed task".

#!/bin/bash -e

RED='\033[0;31m'
NOC='\033[0m'

function _echo() {
  echo -e "${RED}$(date +%d%m%Y-%H:%M:%S) * $1 ${NOC}"
}

function build(){
  typeset distribution=$1
  typeset version=$2

  docker pull ${distribution}:${version}
  docker build --rm=true --file=tests/Dockerfile.${distribution}-${version} --tag=${distribution}-${version}:ansible tests
}

function run() {
  typeset image=$1
  typeset tag=ansible
  typeset site=$2
  typeset run_opts=""
  typeset init=/sbin/init

  _echo "*** $image - $site in progress..."

  container_id=$(mktemp)
  _echo "container_id=${container_id}"

  docker run --detach --volume="${PWD}":/etc/ansible/roles/role_under_test:ro ${run_opts} ${image}:${tag} "${init}" > "${container_id}"
  docker exec "$(cat ${container_id})" ansible-galaxy install -r /etc/ansible/roles/role_under_test/tests/requirements.yml

  _echo "Ansible syntax check."
  docker exec --tty "$(cat ${container_id})" env TERM=xterm ansible-playbook /etc/ansible/roles/role_under_test/tests/$site --syntax-check

  _echo "Test role."
  docker exec --tty "$(cat ${container_id})" env TERM=xterm ansible-playbook /etc/ansible/roles/role_under_test/tests/$site --verbose

  _echo "Test role 2."
  docker exec --tty "$(cat ${container_id})" env TERM=xterm ansible-playbook /etc/ansible/roles/role_under_test/tests/$site --verbose

  _echo "Test role idempotence."
  sudo docker exec "$(cat ${container_id})" ansible-playbook /etc/ansible/roles/role_under_test/tests/$site \
    | grep -q 'changed=0.*failed=0' \
    && (echo 'Idempotence test: pass' && exit 0) \
    || (echo 'Idempotence test: fail' && exit 1)

  _echo "*** $image - $site done."
  _echo "container_id=${container_id}: $(cat ${container_id})"
  docker stop $(cat ${container_id}) && docker rm $(cat ${container_id})
}

build centos 6
build ubuntu 12.04
build ubuntu 14.04

for dist in ubuntu-14.04 ubuntu-12.04 centos-6
do
  for test in test.yml test-http-port.yml test-prefix.yml
  do
    run $dist $test
  done
done

Install Jenkins plugins using password failed

TASK [geerlingguy.jenkins : Install Jenkins plugins using password.] ***********
failed: [ec2-52-213-150-14.eu-west-1.compute.amazonaws.com] (item=git-parameter) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "git-parameter", "--username", "admin", "--password", "admin"], "delta": "0:00:07.937499", "end": "2016-12-25 18:32:26.503943", "failed": true, "item": "git-parameter", "rc": 255, "start": "2016-12-25 18:32:18.566444", "stderr": "Dec 25, 2016 6:32:26 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run\nSEVERE: I/O error in channel Chunked connection to http://localhost:8080/cli\njava.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:73)\nCaused by: java.io.EOFException\n\tat java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)\n\tat java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)\n\tat java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)\n\tat java.io.ObjectInputStream.(ObjectInputStream.java:301)\n\tat hudson.remoting.ObjectInputStreamEx.(ObjectInputStreamEx.java:48)\n\tat hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:59)\n\nhudson.remoting.RequestAbortedException: java.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.Request.abort(Request.java:307)\n\tat hudson.remoting.Channel.terminate(Channel.java:888)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:92)\n\tat ......remote call to Chunked connection to http://localhost:8080/cli(Native Method)\n\tat hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1537)\n\tat hudson.remoting.Request.call(Request.java:172)\n\tat hudson.remoting.Channel.call(Channel.java:821)\n\tat hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:256)\n\tat com.sun.proxy.$Proxy3.main(Unknown Source)\n\tat hudson.cli.CLI.execute(CLI.java:333)\n\tat hudson.cli.CLI._main(CLI.java:504)\n\tat hudson.cli.CLI.main(CLI.java:385)\nCaused by: java.io.IOException: Unexpected termination of the channel\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:73)\nCaused by: java.io.EOFException\n\tat java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)\n\tat java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)\n\tat java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)\n\tat java.io.ObjectInputStream.(ObjectInputStream.java:301)\n\tat hudson.remoting.ObjectInputStreamEx.(ObjectInputStreamEx.java:48)\n\tat hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)\n\tat hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:59)", "stdout": "Installing git-parameter from update center", "stdout_lines": ["Installing git-parameter from update center"], "warnings": []}

Unable to install Plugins on v2

TASK [geerlingguy.jenkins : Install Jenkins plugins.] **************************
failed: [*******] (item=cloudbees-folder) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "cloudbees-folder", "--username", "admin", "--password", "admin"], "delta": "0:00:00.151930", "end": "2016-07-25 19:42:42.309683", "failed": true, "item": "cloudbees-folder", "rc": 255, "start": "2016-07-25 19:42:42.157753", "stderr": "java.io.IOException: No X-Jenkins-CLI2-Port among [null, X-Required-Permission, X-Jenkins, X-You-Are-In-Group, X-Hudson, Content-Length, Expires, X-You-Are-Authenticated-As, X-Permission-Implied-By, Set-Cookie, Server, X-Content-Type-Options, Date, X-Jenkins-Session, Content-Type]\n\tat hudson.cli.CLI.getCliTcpPort(CLI.java:284)\n\tat hudson.cli.CLI.<init>(CLI.java:128)\n\tat hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)\n\tat hudson.cli.CLI._main(CLI.java:473)\n\tat hudson.cli.CLI.main(CLI.java:384)\n\tSuppressed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8080/cli\n\t\tat sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1628)\n\t\tat hudson.cli.FullDuplexHttpStream.<init>(FullDuplexHttpStream.java:78)\n\t\tat hudson.cli.CLI.connectViaHttp(CLI.java:152)\n\t\tat hudson.cli.CLI.<init>(CLI.java:132)\n\t\t... 3 more", "stdout": "", "stdout_lines": [], "warnings": []}
    to retry, use: --limit @./jenkins.retry

Tried rerunning after creating the default user admin and same result. Guessing the hook changed in v2.

Vars prefix

Hi,

I have a suggestion: it would be great to have prefix on your vars like this:

  • jenkins_hostname

Thanks

Not working with Vagrant + Ubuntu trusty

I don't know if this is actually related to Vagrant, but since the build on Travis CI is passing I'm assuming that this is the difference that is breaking my provisioning.

I wanted to use Vagrant + ansible-role-jenkins to setup a Jenkins so I could do some tests with it. But sadly it is breaking on the Add Jenkins apt repository key task.

The error is:

failed: [default] => {"failed": true, "item": ""}
msg: Failed to validate the SSL certificate for jenkins-ci.org:443. Use validate_certs=no or make sure your managed systems have a valid CA certificate installed. Paths checked for this platform: /etc/ssl/certs, /etc/pki/ca-trust/extracted/pem, /etc/pki/tls/certs, /usr/share/ca-certificates/cacert.org, /etc/ansible

FATAL: all hosts have already failed -- aborting

I don't know why a certificate should be missing, I'm using a default ubuntu/trusty box. The whole project is here:

https://github.com/katcipis/jenkins-playground

I checked out the paths and there is a bunch of certificates there :-). Am I missing something ?

Scope of this role regarding plugins

Hi,
I am currently enjoying your jenkins role, and was wondering whether you would regard to provide basic configurations for plugins in scope for this role?

Consider this example:

jenkins_plugins:
  - { id: ssh-credentials }
  - { id: credentials, configSrc: files/credentials.xml, configDest: credentials.xml }
  - { id: email-ext, configTemplate: templates/hudson.plugins.emailext.ExtendedEmailPublisher.xml.jj2, configDest: hudson.plugins.emailext.ExtendedEmailPublisher.xml }

The point is the initial installation of the plugins is not the problem compared to configuring them.

Customise JENKINS_HOME

Is it possible to change the value of JENKINS_HOME as i would like that be on dedicate volume rather then root volume

Installation doesn't work when hostname is accessible via https only

shell: "curl -D - --silent --max-time 5 http://{{ jenkins_hostname }}:{{ jenkins_http_port }}{{ jenkins_url_prefix }}/cli/"

This assumes http is always available. Since the playbook runs on the target server, I suggest changing http://{{ jenkins_hostname }} to http://localhost or provide an option to configure the protocol.

Switch to Docker-based tests

Current tests all run on Travis' Ubuntu 12.04 infra. I'd like to run the tests in Docker containers so I can test on at least CentOS 7 and Ubuntu 14.04 (and maybe other OSes too).

Add support for updating jenkins

Use case:

  • I manage my jenkins installation with a playbook using this role
  • I installed it a week ago or so
  • Now there's a new LTS version of jenkins available
  • Running the playbook again will update the apt cache, but will not update jenkins

This is due to the task “Install Jenkins from repository.” (both apt and rpm versions) which only request the package to be installed. Switching to “latest” would upgrade the package if a new version is available.

It might not be the best for everyone, so maybe an option could be added to select the behaviour between “installed” and “latest”.

"Update apt cache - Permission denied

I'm running a pretty plain Ubuntu 16.04.1 LTS. My playbook is

---
- hosts: log
  roles:
    - geerlingguy.jenkins

and I get the following output:

PLAY [log] *********************************************************************

TASK [setup] *******************************************************************
ok: [log]

TASK [geerlingguy.java : Include OS-specific variables.] ***********************
ok: [log]

TASK [geerlingguy.java : Include OS-specific variables for Fedora.] ************
skipping: [log]

TASK [geerlingguy.java : Include version-specific variables for Ubuntu.] *******
ok: [log]

TASK [geerlingguy.java : Define java_packages.] ********************************
ok: [log]

TASK [geerlingguy.java : include] **********************************************
skipping: [log]

TASK [geerlingguy.java : include] **********************************************
included: /usr/local/etc/ansible/roles/geerlingguy.java/tasks/setup-Debian.yml for log

TASK [geerlingguy.java : Update apt cache.] ************************************
fatal: [log]: FAILED! => {"changed": false, "cmd": "apt-get update", "failed": true, "msg": "W: chmod 0700 of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (1: Operation not permitted)\nE: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\nE: Unable to lock directory /var/lib/apt/lists/\nW: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)\nW: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)\nE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)\nE: Unable to lock the administration directory (/var/lib/dpkg/), are you root?", "rc": 100, "stderr": "W: chmod 0700 of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (1: Operation not permitted)\nE: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\nE: Unable to lock directory /var/lib/apt/lists/\nW: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)\nW: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)\nE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)\nE: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n", "stdout": "", "stdout_lines": []}
	to retry, use: --limit @/Users/sebastian/projects/wfa-dev-ops/ansible/logserver.retry

PLAY RECAP *********************************************************************
log                        : ok=5    changed=0    unreachable=0    failed=1

This can be circumvented by changing the playbook to:

---
- hosts: log
  roles:
    - role: geerlingguy.jenkins
      become: yes

but then I encounter the following error in a later step:

PLAY [log] *********************************************************************

TASK [setup] *******************************************************************
ok: [log]

TASK [geerlingguy.java : Include OS-specific variables.] ***********************
ok: [log]

TASK [geerlingguy.java : Include OS-specific variables for Fedora.] ************
skipping: [log]

TASK [geerlingguy.java : Include version-specific variables for Ubuntu.] *******
ok: [log]

TASK [geerlingguy.java : Define java_packages.] ********************************
ok: [log]

TASK [geerlingguy.java : include] **********************************************
skipping: [log]

TASK [geerlingguy.java : include] **********************************************
included: /usr/local/etc/ansible/roles/geerlingguy.java/tasks/setup-Debian.yml for log

TASK [geerlingguy.java : Update apt cache.] ************************************
ok: [log]

TASK [geerlingguy.java : Ensure Java is installed.] ****************************
changed: [log] => (item=[u'openjdk-8-jdk'])

TASK [geerlingguy.java : include] **********************************************
skipping: [log]

TASK [geerlingguy.java : Set JAVA_HOME if configured.] *************************
skipping: [log]

TASK [geerlingguy.jenkins : Include OS-Specific variables] *********************
ok: [log]

TASK [geerlingguy.jenkins : Define jenkins_repo_url] ***************************
ok: [log]

TASK [geerlingguy.jenkins : Define jenkins_repo_key_url] ***********************
ok: [log]

TASK [geerlingguy.jenkins : Define jenkins_pkg_url] ****************************
ok: [log]

TASK [geerlingguy.jenkins : Ensure dependencies are installed.] ****************
skipping: [log]

TASK [geerlingguy.jenkins : Ensure Jenkins repo is installed.] *****************
skipping: [log]

TASK [geerlingguy.jenkins : Add Jenkins repo GPG key.] *************************
skipping: [log]

TASK [geerlingguy.jenkins : Download specific Jenkins version.] ****************
skipping: [log]

TASK [geerlingguy.jenkins : Check if we downloaded a specific version of Jenkins.] ***
skipping: [log]

TASK [geerlingguy.jenkins : Install our specific version of Jenkins.] **********
skipping: [log]

TASK [geerlingguy.jenkins : Validate Jenkins is installed and register package name.] ***
skipping: [log]

TASK [geerlingguy.jenkins : Install Jenkins from repository.] ******************
skipping: [log]

TASK [geerlingguy.jenkins : Ensure dependencies are installed.] ****************
ok: [log]

TASK [geerlingguy.jenkins : Add Jenkins apt repository key.] *******************
changed: [log]

TASK [geerlingguy.jenkins : Add Jenkins apt repository.] ***********************
changed: [log]

TASK [geerlingguy.jenkins : Download specific Jenkins version.] ****************
skipping: [log]

TASK [geerlingguy.jenkins : Check if we downloaded a specific version of Jenkins.] ***
ok: [log]

TASK [geerlingguy.jenkins : Install our specific version of Jenkins.] **********
skipping: [log]

TASK [geerlingguy.jenkins : Validate Jenkins is installed and register package name.] ***
changed: [log]

TASK [geerlingguy.jenkins : Install Jenkins from repository.] ******************
ok: [log]

TASK [geerlingguy.jenkins : Modify variables in init file] *********************
changed: [log] => (item={u'option': u'JENKINS_ARGS', u'value': u'--prefix='})
changed: [log] => (item={u'option': u'JAVA_ARGS', u'value': u'-Djenkins.install.runSetupWizard=false'})

TASK [geerlingguy.jenkins : Set the Jenkins home directory] ********************
changed: [log]

TASK [geerlingguy.jenkins : Immediately restart Jenkins on init config changes.] ***
changed: [log]

TASK [geerlingguy.jenkins : Set HTTP port in Jenkins config.] ******************
ok: [log]

TASK [geerlingguy.jenkins : Create custom init scripts directory.] *************
changed: [log]

RUNNING HANDLER [geerlingguy.jenkins : configure default users] ****************
fatal: [log]: FAILED! => {"changed": true, "failed": true, "msg": "Destination /var/lib/jenkins/init.groovy.d not writable"}
	to retry, use: --limit @/Users/sebastian/projects/wfa-dev-ops/ansible/logserver.retry

PLAY RECAP *********************************************************************
log                        : ok=22   changed=8    unreachable=0    failed=1  

The directory is owned by jenkins:jenkins.

Any help or pointers appreciated!

Edit

If I add become_method: sudo and run the task a second time, it succeeds.

Edit 2

Only because the failing configure default users handler is skipped :-/

Problems when I install jenkins plugins

**Hello,

I have installed jenkins plugin:**

My jenkins configuration in config.yml

jenkins configuration.

jenkins_connection_delay: 5
jenkins_connection_retries: 60
jenkins_hostname: localhost
jenkins_repo_url: deb http://pkg.jenkins-ci.org/debian-stable binary/
jenkins_repo_key_url: http://pkg.jenkins-ci.org/debian-stable/jenkins-ci.org.key
jenkins_http_port: 8080
jenkins_jar_location: /opt/jenkins-cli.jar
jenkins_plugins:

  • git
  • sonar
  • ssh
    jenkins_url_prefix: ""
    jenkins_admin_username: admin
    jenkins_admin_password: admin

And the vagrant up said me:

TASK [geerlingguy.jenkins : Install Jenkins plugins using password.] ***********
failed: drupaltest => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "git", "--username", "admin", "--password", "admin"], "delta": "0:00:00.133402", "end": "2016-08-19 08:51:39.478404", "failed": true, "item": "git", "rc": 255, "start": "2016-08-19 08:51:39.345002", "stderr": "java.io.IOException: No X-Jenkins-CLI2-Port among [X-Jenkins, X-Required-Permission, null, X-You-Are-In-Group, Date, X-Hudson, Content-Length, Expires, X-You-Are-Authenticated-As, X-Jenkins-Session, Set-Cookie, Content-Type, Server, X-Content-Type-Options]\n\tat hudson.cli.CLI.getCliTcpPort(CLI.java:284)\n\tat hudson.cli.CLI.(CLI.java:128)\n\tat hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)\n\tat hudson.cli.CLI._main(CLI.java:473)\n\tat hudson.cli.CLI.main(CLI.java:384)\n\tSuppressed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8080/cli\n\t\tat sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1628)\n\t\tat hudson.cli.FullDuplexHttpStream.(FullDuplexHttpStream.java:78)\n\t\tat hudson.cli.CLI.connectViaHttp(CLI.java:152)\n\t\tat hudson.cli.CLI.(CLI.java:132)\n\t\t... 3 more", "stdout": "", "stdout_lines": [], "warnings": []}
failed: drupaltest => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "sonar", "--username", "admin", "--password", "admin"], "delta": "0:00:00.125593", "end": "2016-08-19 08:51:39.799369", "failed": true, "item": "sonar", "rc": 255, "start": "2016-08-19 08:51:39.673776", "stderr": "java.io.IOException: No X-Jenkins-CLI2-Port among [X-Jenkins, X-Required-Permission, null, X-You-Are-In-Group, Date, X-Hudson, Content-Length, Expires, X-You-Are-Authenticated-As, X-Jenkins-Session, Set-Cookie, Content-Type, Server, X-Content-Type-Options]\n\tat hudson.cli.CLI.getCliTcpPort(CLI.java:284)\n\tat hudson.cli.CLI.(CLI.java:128)\n\tat hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)\n\tat hudson.cli.CLI._main(CLI.java:473)\n\tat hudson.cli.CLI.main(CLI.java:384)\n\tSuppressed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8080/cli\n\t\tat sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1628)\n\t\tat hudson.cli.FullDuplexHttpStream.(FullDuplexHttpStream.java:78)\n\t\tat hudson.cli.CLI.connectViaHttp(CLI.java:152)\n\t\tat hudson.cli.CLI.(CLI.java:132)\n\t\t... 3 more", "stdout": "", "stdout_lines": [], "warnings": []}
failed: drupaltest => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://localhost:8080/", "install-plugin", "ssh", "--username", "admin", "--password", "admin"], "delta": "0:00:00.135267", "end": "2016-08-19 08:51:40.111703", "failed": true, "item": "ssh", "rc": 255, "start": "2016-08-19 08:51:39.976436", "stderr": "java.io.IOException: No X-Jenkins-CLI2-Port among [X-Jenkins, X-Required-Permission, null, X-You-Are-In-Group, Date, X-Hudson, Content-Length, Expires, X-You-Are-Authenticated-As, X-Jenkins-Session, Set-Cookie, Content-Type, Server, X-Content-Type-Options]\n\tat hudson.cli.CLI.getCliTcpPort(CLI.java:284)\n\tat hudson.cli.CLI.(CLI.java:128)\n\tat hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)\n\tat hudson.cli.CLI._main(CLI.java:473)\n\tat hudson.cli.CLI.main(CLI.java:384)\n\tSuppressed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8080/cli\n\t\tat sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1628)\n\t\tat hudson.cli.FullDuplexHttpStream.(FullDuplexHttpStream.java:78)\n\t\tat hudson.cli.CLI.connectViaHttp(CLI.java:152)\n\t\tat hudson.cli.CLI.(CLI.java:132)\n\t\t... 3 more", "stdout": "", "stdout_lines": [], "warnings": []}

NO MORE HOSTS LEFT *************************************************************
to retry, use: --limit @/vagrant/provisioning/playbook.retry

PLAY RECAP *********************************************************************

I have vagrant 1.8.5 in windows 10 :-)

Avoid 2.0 setup wizard but provide secure-by-default configuration

Jenkins < 2.0 was insecure-by-default.

Jenkins 2.0 introduced a mandatory setup wizard that makes the installation secure-by-default.

But for CD/CI of Jenkins itself (via Ansible or other tools), you need to pass the -Djenkins.install.runSetupWizard=false option in either global JAVA_OPTS or in JENKINS_JAVA_OPTIONS in the Jenkins init file. Due to reasons stated in #47, though, using that option is not consistent/easy in all OS environments (e.g. Debian/Ubuntu)... and it also (if just using that option) makes the Jenkins installation insecure-by-default.

I'd like to skip the setup wizard yet still provide a secured installation, and also make sure the 2.0 install works the same across Ubuntu 12/14/16 and CentOS 6/7.

I'd also like to get the CLI plugin installation working again, but that could be broken out into a separate issue.

See related discussions/posts:

Wait for Jenkins to start up before proceeding - fails on a dry run

Hi @geerlingguy,

Context

The step "Wait for Jenkins to start up before proceeding." fails when you invoke this role with a playbook in --check mode (dry run). This doesn't happen on the normal run.

Expected Behavior

Continue the checks after verifying that the Jenkins instance is up.

Actual Behavior

Ansible fails with:

FAILED! => {"failed": true, "msg": "The conditional check '(result.stdout.find("403 Forbidden") != -1) or (result.stdout.find("200 OK") != -1) and (result.stdout.find("Please wait while") == -1)' failed. The error was: error while evaluating conditional ((result.stdout.find("403 Forbidden") != -1) or (result.stdout.find("200 OK") != -1) and (result.stdout.find("Please wait while") == -1)): 'dict object' has no attribute 'stdout'"}

Steps to Reproduce

  1. Include this role on a playbook
  2. Specify jenkins_version as 2.19.1
  3. Specify jenkins_pkg_url as http://pkg.jenkins-ci.org/debian-stable/binary/
  4. Set a jenkins_admin_username and a jenkins_admin_password together with the normal suggested plugins on jenkins_plugins
  5. Do a normal run, let it install
  6. Now do a dry run with --check

Environment

  • Version used: Ansible 2.1.2.0
  • OS where Ansible runs from: macOS 10.12.1
  • Target OS: Debian 8 (Vagrant)

Debian based os fail to install plugins when using Jenkins v2+

Based on the conversation in #70, there are issues with the way that the cli interacts on Debian based OS'. The current workaround is to add in:

jenkins_init_changes:
  - option: "JAVA_ARGS"
    value: "-Dhudson.diyChunking=false"

to your playbook vars, which overrides any of the other defaults that do exist. It also requires the user to know that they need to add that in, since it's not readily apparent why it's not working.

Because this appears to be an upstream issue, having to get around this in any sane way can be hackish because it requires changes to the ansible role that have little to do with Jenkins in general. Providing a good user experience, however, may be appropriate since there appears to be a workaround in place.

Options at hand are to split out settings and have them separate for rpm/deb based distros and merge the defaults, with user configs. Alternatively, there's the option to have an option, called merge_jenkins_defaults which would then do that merge.

provision for setting jnlp port.

the role is unable to install jenkins-plugins because jnlp port is not configured.
A blog bost by voidtech [1], helps solve the issue but requires manual step which I am unable to automate via ansible.

TASK: [geerlingguy.jenkins | Install Jenkins plugins.] ************************
failed: [ci-server1] => (item=git) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://X.Y.Z.W:8080/", "install-plugin", "git"], "delta": "0:00:03.872323", "end": "2015-10-26 17:45:50.800905", "item": "git", "rc": 255, "start": "2015-10-26 17:45:46.928582", "warnings": []}
stderr: Oct 26, 2015 5:45:50 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run
SEVERE: I/O error in channel Chunked connection to http://X.Y.Z.W:8080/cli
java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

hudson.remoting.RequestAbortedException: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at hudson.remoting.Request.abort(Request.java:297)
    at hudson.remoting.Channel.terminate(Channel.java:844)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:69)
    at ......remote call to Chunked connection to http://X.Y.Z.W:8080/cli(Native Method)
    at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1413)
    at hudson.remoting.Request.call(Request.java:172)
    at hudson.remoting.Channel.call(Channel.java:777)
    at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:250)
    at hudson.remoting.$Proxy1.waitForProperty(Unknown Source)
    at hudson.remoting.Channel.waitForRemoteProperty(Channel.java:1255)
    at hudson.cli.CLI.<init>(CLI.java:147)
    at hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)
    at hudson.cli.CLI._main(CLI.java:479)
    at hudson.cli.CLI.main(CLI.java:390)
Caused by: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)
failed: [ci-server1] => (item=sonar) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://X.Y.Z.W:8080/", "install-plugin", "sonar"], "delta": "0:00:03.513631", "end": "2015-10-26 17:45:54.961781", "item": "sonar", "rc": 255, "start": "2015-10-26 17:45:51.448150", "warnings": []}
stderr: Oct 26, 2015 5:45:54 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run
SEVERE: I/O error in channel Chunked connection to http://X.Y.Z.W:8080/cli
java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

hudson.remoting.RequestAbortedException: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at hudson.remoting.Request.abort(Request.java:297)
    at hudson.remoting.Channel.terminate(Channel.java:844)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:69)
    at ......remote call to Chunked connection to http://X.Y.Z.W:8080/cli(Native Method)
    at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1413)
    at hudson.remoting.Request.call(Request.java:172)
    at hudson.remoting.Channel.call(Channel.java:777)
    at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:250)
    at hudson.remoting.$Proxy1.waitForProperty(Unknown Source)
    at hudson.remoting.Channel.waitForRemoteProperty(Channel.java:1255)
    at hudson.cli.CLI.<init>(CLI.java:147)
    at hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)
    at hudson.cli.CLI._main(CLI.java:479)
    at hudson.cli.CLI.main(CLI.java:390)
Caused by: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)
failed: [ci-server1] => (item=ssh) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://X.Y.Z.W:8080/", "install-plugin", "ssh"], "delta": "0:00:03.467868", "end": "2015-10-26 17:46:00.109269", "item": "ssh", "rc": 255, "start": "2015-10-26 17:45:56.641401", "warnings": []}
stderr: Oct 26, 2015 5:46:00 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run
SEVERE: I/O error in channel Chunked connection to http://X.Y.Z.W:8080/cli
java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

hudson.remoting.RequestAbortedException: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at hudson.remoting.Request.abort(Request.java:297)
    at hudson.remoting.Channel.terminate(Channel.java:844)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:69)
    at ......remote call to Chunked connection to http://X.Y.Z.W:8080/cli(Native Method)
    at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1413)
    at hudson.remoting.Request.call(Request.java:172)
    at hudson.remoting.Channel.call(Channel.java:777)
    at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:250)
    at hudson.remoting.$Proxy1.waitForProperty(Unknown Source)
    at hudson.remoting.Channel.waitForRemoteProperty(Channel.java:1255)
    at hudson.cli.CLI.<init>(CLI.java:147)
    at hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)
    at hudson.cli.CLI._main(CLI.java:479)
    at hudson.cli.CLI.main(CLI.java:390)
Caused by: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)
failed: [ci-server1] => (item=github) => {"changed": true, "cmd": ["java", "-jar", "/opt/jenkins-cli.jar", "-s", "http://X.Y.Z.W:8080/", "install-plugin", "github"], "delta": "0:00:03.372999", "end": "2015-10-26 17:46:04.243199", "item": "github", "rc": 255, "start": "2015-10-26 17:46:00.870200", "warnings": []}
stderr: Oct 26, 2015 5:46:04 PM hudson.remoting.SynchronousCommandTransport$ReaderThread run
SEVERE: I/O error in channel Chunked connection to http://X.Y.Z.W:8080/cli
java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

hudson.remoting.RequestAbortedException: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at hudson.remoting.Request.abort(Request.java:297)
    at hudson.remoting.Channel.terminate(Channel.java:844)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:69)
    at ......remote call to Chunked connection to http://X.Y.Z.W:8080/cli(Native Method)
    at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1413)
    at hudson.remoting.Request.call(Request.java:172)
    at hudson.remoting.Channel.call(Channel.java:777)
    at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:250)
    at hudson.remoting.$Proxy1.waitForProperty(Unknown Source)
    at hudson.remoting.Channel.waitForRemoteProperty(Channel.java:1255)
    at hudson.cli.CLI.<init>(CLI.java:147)
    at hudson.cli.CLIConnectionFactory.connect(CLIConnectionFactory.java:72)
    at hudson.cli.CLI._main(CLI.java:479)
    at hudson.cli.CLI.main(CLI.java:390)
Caused by: java.io.StreamCorruptedException: invalid stream header: 0A0A0A0A
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:40)
    at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
    at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

FATAL: all hosts have already failed -- aborting

[1] https://voidtech.wordpress.com/2015/08/26/jenkins-cli-jar-java-io-streamcorruptedexception-invalid-stream-header-0a0a0a0a-solution/

Unlock Jenkins

Hey,

Is there any way to automate Unlock Jenkins via cli? api? ansible?
or maybe i missed something in the default variables
( i know i can copy the initial administrator password from
/var/lib/jenkins/secrets/initialAdminPassword
but i want to automate this procedure)

thanks!

I can not enter to jenkins with user admin and pass admin

Hello,

I put in my config.yml this configuration:

jenkins configuration.

jenkins_connection_delay: 5
jenkins_connection_retries: 60
jenkins_hostname: localhost
jenkins_repo_url: deb http://pkg.jenkins-ci.org/debian-stable binary/
jenkins_repo_key_url: http://pkg.jenkins-ci.org/debian-stable/jenkins-ci.org.key
jenkins_http_port: 8080
jenkins_jar_location: /opt/jenkins-cli.jar
jenkins_plugins:

  • git
  • sonar
  • ssh
    jenkins_url_prefix: ""
    #ssh_home: "{{ drupal_core_path }}"
    jenkins_admin_username: admin
    jenkins_admin_password: admin

But when open the page in browser, I have used admin admin and recive this message:

"Error de autenticación"

I have:

Jenkins ver. 2.7.2 in the role:

Requirements: - src: geerlingguy.jenkins

Playbook: - { role: geerlingguy.jenkins, when: '"jenkins" in installed_extras' }

Windows 10 + cygwin 32 bits

Thanks

ValueError: invalid literal for int() with base 10: jenkins_connection_retries

Used example in README:

- hosts: local
  vars:
      jenkins_hostname: jenkins.example.com
  roles:
      - { role: geerlingguy.jenkins }

With no explicit value for jenkins_connection_retries provided, role fails with next exception:

fatal: [local] => Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/ansible/runner/__init__.py", line 536, in _executor
    exec_rc = self._executor_internal(host, new_stdin)
  File "/usr/lib/pymodules/python2.7/ansible/runner/__init__.py", line 638, in _executor_internal
    return self._executor_internal_inner(host, self.module_name, self.module_args, inject, port, complex_args=complex_args)
  File "/usr/lib/pymodules/python2.7/ansible/runner/__init__.py", line 841, in _executor_internal_inner
    for x in range(1, int(retries) + 1):
ValueError: invalid literal for int() with base 10: '{{jenkins_connection_retries}}'

Wonder if it's missing loading of default variables from /defaults/main.yml file...

Ansible: 1.6.6
Role:
geerlingguy.java, 1.1.0
geerlingguy.jenkins, 1.2.4

Plugins not from jenkins update center

We have to pull a plugin from a non-jenkins site and and would like to be able to use a url to pull down the plugin. What are your thoughts? This ability works fine:

jenkins_plugins:
  - ansible
  - ansicolor
  - name: some-plugin
    url: https://somesite.invalid/downloads/some-plugin.jpi

this works fine with 1.9.1 in the tasks/plugins.yaml

- name: Install Jenkins plugins.
  command: >
    java -jar {{ jenkins_jar_location }}
     -s http://{{ jenkins_hostname }}:8080/
     install-plugin {{ item.url if item.url is defined else item }}
    creates=/var/lib/jenkins/plugins/{{ item.name if item.name is defined else item }}.jpi
  with_items: jenkins_plugins
  notify: restart jenkins

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.