Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Puppet vs. Jenkins: A Tale of Types and Providers

Puppet vs. Jenkins: A Tale of Types and Providers

PuppetConf 2015

Avatar for Joshua Hoblitt

Joshua Hoblitt

October 08, 2015
Tweet

More Decks by Joshua Hoblitt

Other Decks in Technology

Transcript

  1. Puppet vs. Jenkins: A Tale of Types and Providers Joshua

    Hoblitt DevOps Engineer | Large Synoptic Survey Telescope @jhoblitt
  2. Puppet & Me • Puppet user since 2010 – 0.25

    • 30 modules published the forge – 2 Puppetlabs “Approved” • PRs merged into ~37 puppet related repos on github
  3. Index • Prologue • Jenkins • Puppet Disclaimer: this talk

    assumes an understanding of implementing types and providers
  4. “Config” file format <securityRealm class="hudson.security.HudsonPrivateSecurityRealm"> <disableSignup>false</disableSignup> <enableCaptcha>false</enableCaptcha> </securityRealm> private final

    boolean disableSignup; private final boolean enableCaptcha; public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha, CaptchaSupport captchaSupport) { this.disableSignup = !allowsSignup; this.enableCaptcha = enableCaptcha; setCaptchaSupport(captchaSupport); ... } Example constructor Generated XML fragment
  5. CLI HTTP X-Jenkins-CLI2-Port: 44444 SSH $ ssh ­p 34512 admin@localhost

    ­i ~/id_rsa groovysh Groovy Shell (1.8.9, JVM: 1.7.0_85) Type 'help' or '\h' for help. ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ groovy:000>
  6. CLI groovy • Allows groovy scripts to be run on

    jenkins' JVM • Maps local stdin/stdout to groovy interpreter over CLI connection • Passes additional CLI args to groovy interpreter • Means of working around CLI command limitations $ java ­jar jenkins­cli.jar ­s http://localhost:8080 ­i id_rsa\ groovy puppet_helper.groovy get_num_executors 42 $ java ­jar jenkins­cli.jar ­s http://localhost:8080 ­i id_rsa\ groovy puppet_helper.groovy set_num_executors 3 $ java ­jar jenkins­cli.jar ­s http://localhost:8080 ­i id_rsa\ groovy puppet_helper.groovy get_num_executors 3
  7. CLI or Authentication $ java ­jar jenkins­cli.jar ­s http:/localhost:8080 copy­job

    test1 test2 hudson.security.AccessDeniedException2: anonymous is missing the Job/Create permission at hudson.security.ACL.checkPermission(ACL.java:63) … $ java ­jar jenkins­cli.jar ­s http://localhost:8080 list­jobs test1 $ java ­jar jenkins­cli.jar ­i ~/id_rsa ­s http://localhost:8080 copy­job test1 test2 java.io.EOFException at java.io.DataInputStream.readBoolean(DataInputStream.java:244) … $ java ­jar jenkins­cli.jar ­i ~/id_rsa ­s http://localhost:8080 list­jobs java.io.EOFException at java.io.DataInputStream.readBoolean(DataInputStream.java:244) Security Enabled Security Disabled
  8. Setting the API token void user_update() { // or create

    … def api_token_plain = conf['api_token_plain'] if (api_token_plain) { assert api_token_plain instanceof String def token_param = new ApiTokenProperty() token_param.@apiToken = new Secret(api_token_plain) user.addProperty(token_param) } … $ puppet resource –modulepath=/ jenkins_user admin jenkins_user { 'admin': ensure => 'present', api_token_plain => '492b8b1a8a5015b5ff597ee8d4c00a43', api_token_public => 'c7311b827cd5244b603b01f96f4c6e24', full_name => 'jenkins admin', public_keys => ['ssh­rsa ...'], } files/puppet_helper.groovy
  9. Pros & cons of service mediated configuration • Pros –

    Don't have to parse/comprehend configuration format – Clever dynamic actions are possible • Cons – Service has to be up – Authentication may need to be working – Performance – API may be complex – API may be in an unknown language
  10. How might this be better? • Out of band administrative

    mechanism; E.g. domain socket • Non-daemon mode for configuration manipulation
  11. Goals • Extend existing puppet­jenkins module • Common configuration between

    resources • Resolve CLI – manifest authentication bootstrapping • Configure the github­oauth plugin • Manage user API tokens so that github­oauth does not cut off swarm slaves
  12. Common resource configuration • Could not identify any examples –

    puppetlabs­aws relies on aws­sdk to pull credentials from the environment • DSL has resource defaults – Scope issues :( • Providers are limited to facts and introspecting on the catalog
  13. PuppetX::Jenkins::Config # manifest.pp class jenkins::cli::config( $cli_jar = undef, $port =

    undef, $ssh_private_key = undef, $puppet_helper = undef, $cli_tries = undef, $cli_try_sleep = undef, ) {} class { 'jenkins::cli::config': ssh_private_key => '/tmp/id_rsa', } # provider/jenkins_foo/cli.rb config = PuppetX::Jenkins::Config.new(catalog) ssh_private_key = config[:ssh_private_key]
  14. Accessing the Catalog • Accessing the catalog from a provider

    not attached to a resource is difficult – Adrien/Hunter axis-of-evil-puppet-constructs pointed out that ::prefetch is called with a list of resources – resources are linked to the catalog
  15. class PuppetX::Jenkins::Provider::Cli < Puppet::Provider def self.prefetch(resources) catalog = resources.first[1].catalog instances(catalog).each

    do |prov| if resource = resources[prov.name] resource.provider = prov end end end end Puppet::Type.type(:jenkins_user).provide(:cli, :parent => PuppetX::Jenkins::Provider::Cli) do def self.instances(catalog = nil) all = user_info_all(catalog) all.collect {|info| from_hash(info)} end end
  16. Resource face export FACTER_jenkins_puppet_helper=/jenkins/files/puppet_helper.groovy export FACTER_jenkins_cli_tries=2 export FACTER_jenkins_ssh_private_key=/id_rsa $ puppet

    resource ­­modulepath=/ jenkins_user ­­debug –trace Debug: Runtime environment: puppet_version=3.8.2, ruby_version=2.0.0, ru … jenkins_user { 'admin': ensure => 'present', api_token_plain => '6107391bb75109e6baf78762399da35d', api_token_public => '62725bc3ffcfdf99a86bc7b9982f7957', full_name => 'jenkins admin', public_keys => ['ssh­rsa …'], } • Extremely useful for debugging • No manifest is parsed; facts to pass data
  17. provider inheritance • Provider confine(s) inherit in a broken state

    • ::commands is ultimately a ::confine • This is kludged around in the core providers Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Provider::Package do commands :rpm => "rpm" end Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do commands :yum => "yum", :rpm => "rpm" end
  18. class PuppetX::Jenkins::Provider::Cli < Puppet::Provider # push a shallow copy of

    the class confines into the subclass # this includes the confine(s) needed for commands # # The subclass seems to function with an empty @commands and any # value we try to push in will get reselt by ::initvars. def self.inherited(subclass) subclass.instance_variable_set(:@confine_collection, @confine_collection.dup) end commands :java => 'java' confine :feature => :retries end
  19. Shared State class PuppetX::Jenkins::Provider::Cli < Puppet::Provider def self.execute_with_auth(cli_cmd, auth_cmd, options

    = {}) ... # we already know that auth is required if class_variable_get(:@@cli_auth_required) return execute_exceptionify(auth_cmd, options) end begin # try first with no auth return execute_exceptionify(cli_cmd, options) rescue AuthError # retry with auth result = execute_exceptionify(auth_cmd, options) class_variable_set(:@@cli_auth_required, true) return result end end end `
  20. Resource ordering • All types need service resource and ssh

    & puppet_helper to be converged • Implemented with a simple wrapper around Puppet::Type::newtype() module PuppetX::Jenkins::Type::Cli def self.newtype(*args, &block) type = Puppet::Type.newtype(*args, &block) type.autorequire(:service) do ['jenkins'] end ... end end PuppetX::Jenkins::Type::Cli.newtype(:jenkins_job) do @doc = "Manage Jenkins' jobs" … end
  21. Putting it all together • Ab-use the params of a

    class as a data source – Use ::prefetch to access the catalog and implement ::instances with a catalog argument – Fall back on facts to support puppet resource • Provider base class – “fixes” inheritance of confine – Shared state via class variables • Type factory wrapper for common autorequire
  22. Status of TPification • Merged but not shipped – Release

    coming “real soon” • Need real-world usage feedback • Still needs to be integrated with existing defined types – Some semantics have changed (fixed) • Need acceptance tests that cover interaction with puppet_helper.groovy
  23. Pain points • No formal mechanism for passing data in

    catalog • No formal means of sharing information between providers • Accessing the catalog can be difficult from a provider • Singleton resources – DSL has classes – No core mechanism for types • Attempting to limit type/provider instances causes fallout • Provider inheritance is funky • Understanding ::prefetch, ::flush, ::instances, etc. requires reading the source – Beware newtype meta-dragons • Types can not be made private to a module • Types are not namespaces - jenkins_credentials instead of jenkins::credentials – Prevents direct conversion between DSL and native types
  24. Links • github.com/jenkinsci/puppet-jenkins - bit.ly/puppet-jenkins • NATIVE_TYPES_AND_PROVIDERS.md - bit.ly/tp-readme •

    github.com/jenkinsci/github-oauth-plugin - bit.ly/github-oauth-plugin • freenode: ^conner • Twitter: @jhoblitt • joshua.hoblitt.com/ Contact