ensure tomcat - duplicated definition

93 views
Skip to first unread message

Björn Becker

unread,
Apr 23, 2014, 1:43:24 PM4/23/14
to puppet...@googlegroups.com
Hello,

I fight with a tomcat module and try to ensure several tomcat instances:

node XY{
  include tomcat
    ensure_tomcats{
      "tomcat1":
        tc_name => "tomcat1",
        tc_path => "/app/tomcat1/tomcat",
        version => "7.0.53";
      "tomcat2":
        tc_name => "tomcat2",
        tc_path => "/app/tomcat2/tomcat",
        version => "7.0.53";
}

I guess that my module isn't best practice:

#cat tomcat/manifests/init.pp

define download($version=""){
    file{"download apache-tomcat-$version":
      path   => "/usr/local/src/apache-tomcat-$version.tar.gz",
      ensure => present,
      owner  => $tc_name,
      group  => app,
      mode   => "0755",
      source => "puppet:///modules/tomcat/apache-tomcat-$version.tar.gz",
    }
}

define ensure_tomcats($tc_name, $tc_path, $version) {
    download{"receive $tc_name $version":
      version => $version,
    }

    file{"$tc_path":
      ensure  => present,
      owner   => $tc_name,
      group   => "app",
      recurse => true,
      require => Exec["$tc_name install"],
    }

    exec{"$tc_name extract apache-tomcat-$version":
      command => "/bin/tar xfz /usr/local/src/apache-tomcat-$version.tar.gz",
      cwd     => "/usr/local/src",
      creates => "/usr/local/src/apache-tomcat-$version",
      user    => "root",
      require => File["download apache-tomcat-$version"],
    }

    exec{"$tc_name install":
      command => "/bin/cp -r /usr/local/src/apache-tomcat-$version $tc_path",
      cwd     => "/usr/local/src",
      creates => "$tc_path",
      user    => "root",
      require => [ Exec["$tc_name extract apache-tomcat-$version"], ],
    }

    file{"$tc_path/webapps/examples":
      ensure  => absent,
      force   => true,
      require => Exec["$tc_name install"],
    }

    file{"$tc_path/webapps/docs":
      ensure  => absent,
      force   => true,
      require => Exec["$tc_name install"],
    }

    file{"/app/para/$tc_name.para":
      ensure => present,
      owner  => "root",
      group  => "app",
      mode   => "0644",
      source => [ "puppet:///modules/tomcat/$tc_name.para.$::hostname", "puppet:///modules/tomcat/$tc_name.para" ],
    }

    user{"$tc_name":
      ensure           => present,
      home             => $tc_path,
      managehome       => true,
      groups           => "app",
    }

    file{"tomcat-users $tc_name":
      ensure => file,
      path   => "${tc_path}/conf/tomcat-users.xml",
      owner  => $tc_name,
      group  => "app",
      source => [
        "puppet:///modules/tomcat/$::hostname.tomcat-users.xml",
        "puppet:///modules/tomcat/tomcat-users.xml",
      ],
      require => Exec["$tc_name install"],
    }

    file{"server.xml $tc_name":
      ensure => file,
      path   => "${tc_path}/conf/server.xml",
      owner  => $tc_name,
      group  => "app",
      source => [
        "puppet:///modules/tomcat/$::hostname.$tc_name.server.xml",
        "puppet:///modules/tomcat/$tc_name.server.xml",
        "puppet:///modules/tomcat/server.xml",
      ],
      notify => Service["$tc_name"],
      require => Exec["$tc_name install"],
    }

    file{"manager web.xml $tc_name":
      ensure => file,
      path   => "${tc_path}/webapps/manager/WEB-INF/web.xml",
      owner  => $tc_name,
      group  => "app",
      source => [
        "puppet:///modules/tomcat/$::hostname.$tc_name.manager.web.xml",
        "puppet:///modules/tomcat/$::hostname.manager.web.xml",
        "puppet:///modules/tomcat/manager.web.xml",
      ],
      notify => Service["$tc_name"],
      require => Exec["$tc_name install"],
    }

    file{"setenv.sh $tc_name":
      ensure => file,
      path   => "${tc_path}/bin/setenv.sh",
      owner  => $tc_name,
      group  => "app",
      mode   => "0750",
      source => [
      "puppet:///modules/tomcat/$::hostname.$tc_name.setenv.sh",
      "puppet:///modules/tomcat/$::hostname.setenv.sh",
      "puppet:///modules/tomcat/setenv.sh",
      ],
      require => Exec["$tc_name install"],
    }

    file {"/etc/init.d/$tc_name":
      content => template('tomcat/tomcat_init.erb'),
      owner   => root,
      group   => root,
      mode    => 0755,
    }

    service{"$tc_name":
      ensure     => running,
      enable     => true,
      hasrestart => true,
      require    => [ File["$tc_path"], File["setenv.sh $tc_name"], File["manager web.xml $tc_name"], File["server.xml $tc_name"], File["/etc/init.d/$tc_name"], ],
    }
}

class tomcat{

  file{'/app/proc/tomcat':
    ensure => present,
    owner  => "root",
    group  => "app",
    mode   => "0750",
    source => "puppet:///modules/tomcat/tomcat",
  }
}

My problem is that if there're two tomcat instances with the same version the module try to download the apache-tomcat tarball every time.

Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Duplicate declaration: File[download apache-tomcat-7.0.53] is already declared in file /etc/puppet/modules/tomcat/manifests/init.pp:9; cannot redeclare at /etc/puppet/modules/tomcat/manifests/init.pp:9 on node YX

Thanks in advance
Björn

William Leese

unread,
Apr 24, 2014, 3:51:13 AM4/24/14
to puppet...@googlegroups.com
Wouldn't it be easier to turn:

file{"download apache-tomcat-$version" ...}

into an Exec (curl/wget) with an unless parameter?

or more like your original design, but limited: turn "define download" into a class that installs only a single version (maybe a separate class per major version, and just pull in the latest minor of that major release) and use the include in your defines to avoid dupl resource errors.

Björn Becker

unread,
Apr 24, 2014, 4:35:29 AM4/24/14
to puppet...@googlegroups.com
Unfortunataly I'm not able to establish the dependency:

define ensure_tomcats($tc_name, $tc_path, $version) {
  include tomcat::download

  download_tomcat{"tomcat $version": version => $version;}


    file{"$tc_path":
      ensure  => present,
      owner   => $tc_name,
      group   => "app",
      recurse => true,
      require => Exec["$tc_name install"],
    }

    exec{"$tc_name extract apache-tomcat-$version":
      command => "/bin/tar xfz /usr/local/src/apache-tomcat-$version.tar.gz",
      cwd     => "/usr/local/src",
      creates => "/usr/local/src/apache-tomcat-$version",
      user    => "root",
      require => File["download apache-tomcat-$version"],
    }
}

class tomcat{

  class download {

    define download_tomcat($version) {

      file{"download apache-tomcat-$version":
        path   => "/usr/local/src/apache-tomcat-$version.tar.gz",
        ensure => present,
        owner  => root,

        group  => app,
        mode   => "0755",
        source => "puppet:///modules/tomcat/apache-tomcat-$version.tar.gz",
      }
    }
  }

  file{'/app/para':
    ensure  => directory,

    owner   => "root",
    group   => "app",
    mode    => "0775",
    recurse => "true",
  }
}

With this class puppet can't find the resource_type download_tomcat.

Felix Frank

unread,
Apr 24, 2014, 8:42:34 AM4/24/14
to puppet...@googlegroups.com
Rule of thumb with defined types: Resources that those declare will want
to have the $title (or $name) or their enclosing defined type in their
title to avoid clashes.

download_tomcat { "tomcat-dl-for-$name": version => $version }

HTH,
Felix

jcbollinger

unread,
Apr 24, 2014, 11:54:00 AM4/24/14
to puppet...@googlegroups.com


On Thursday, April 24, 2014 7:42:34 AM UTC-5, Felix.Frank wrote:
Rule of thumb with defined types: Resources that those declare will want
to have the $title (or $name) or their enclosing defined type in their
title to avoid clashes.

download_tomcat { "tomcat-dl-for-$name": version => $version }



Yes, but in this case there would still be a problem with that because the defined type manages a File whose path depends only on the tomcat version.  That could be resolved by giving each tomcat instance its own, separate tomcat tarball.  That gets a bit messy, however, not to mention wasteful.

Personally, I'd have the tomcat instances share by declaring the tomcat distribution virtually, something like this:

tomcat/manifests/dist.pp:
----
define tomcat::dist() {
  $tc_name = "apache-tomcat-${title}"
  $tarball = "${tc_name}.tar.gz"
  $src_dir = '/usr/local/src'

  file{ "${src_dir}/${tarball}":
        ensure => present,
        owner  => root,

        group  => app,
        mode   => '0755',
        source => "puppet:///modules/tomcat/${tarball}",
  }

  exec{"extract ${tc_name}":
      command => "/bin/tar xzf ${src_dir}/${tarball}",
      cwd     => ${src_dir},
      creates => "${src_dir}/${tc_name}",
      require => File["${src_dir}/${tarball}"]
  }
}


tomcat/manifests/init.pp:
----
class tomcat {
  # Better if the available versions were drawn from external data:
  $available_versions = ['6.0.39', '7.0.53', '8.0.5']

  # Virtual declaration(s):
  @tomcat::dist { $available_versions: }

  ...
}


tomcat/manifests/instance.pp:
----
# renamed from ensure_tomcats:
define tomcat::instance(
  $tc_path,
  $version
) {
  include 'tomcat'

  $tc_name = $title

  # Realize instead of declaring:
  realize Tomcat::Dist[$version]

  exec{"$tc_name install":
    ...
    require => Tomcat::Dist[$version]
  }

  ...
}


manifests/site_or_some_other.pp:
----
node XY{
  # no need to "include tomcat" here
  tomcat::instance {
    'tomcat1':

      tc_path => "/app/tomcat1/tomcat",
      version => "7.0.53";
    'tomcat2':

      tc_path => "/app/tomcat2/tomcat",
      version => "7.0.53";
  }
}



John

jcbollinger

unread,
Apr 24, 2014, 12:08:40 PM4/24/14
to puppet...@googlegroups.com


On Wednesday, April 23, 2014 12:43:24 PM UTC-5, Björn Becker wrote:
I guess that my module isn't best practice:



Actually, yes, it's pretty bad.  My apologies for being blunt.  Specifically,
  1. You are polluting the global namespace with your defined-type names.
  2. Each manifest file in your module should contain exactly one class definition or one defined type.
  3. Defined types should represent resources (at some level of abstraction) as opposed to actions, and their names should be nouns, not verbs.
  4. It is often good practice for defined types to actually use their titles.  On the flip side, it is poor practice to define type parameters that duplicate the title, or to use titles that are redundant with the type name and / or a characteristic property.
Some might also suggest breaking up that big defined type into smaller pieces, as indeed the partial code in my previous response starts to do.  I don't think there's necessarily a clear best practice there, but organizing the module as a collection of smaller pieces could make it easier to understand and maintain.


John

Reply all
Reply to author
Forward
0 new messages