Bug #1472

Defined exported resource are not collected properly.

Added by Brice Figureau over 3 years ago. Updated over 3 years ago.

Status:Closed Start date:07/31/2008
Priority:High Due date:
Assignee:James Turnbull % Done:

0%

Category:transactions
Target version:0.24.6
Affected Puppet version:0.24.4 Branch:
Keywords:exported collect resource define
Votes: 0

Description

Hi, On puppet > 0.24.4 (including 0.24.5), I can’t seem to collect “defined” resource. Native resource are collected fine.

This doesn’t work:

# Non-working site.pp

define deftest ()
{
   warning("DEFTEST ${fqdn} on ${name}")
   file { "${name}":
     content => "test"
   }
}

node "xen1.internal" {
  $part = "xen1"

  @@deftest {
     "/tmp/${part}":
       tag => "test"
  }
  Deftest <<| tag=='test' |>>
}

node "xen2.internal" {
  $part = "xen2"

  @@deftest {
    "/tmp/${part}":
       tag => "test"
  }
  Deftest <<| tag=='test' |>>
}

Proof: running manually on xen1.internal gives:

info: Loading fact netmask
info: Loading fact ldap_auth_bind_user
info: Loading fact configured_ntp_servers
info: Retrieving plugins
info: Loading fact netmask
info: Loading fact ldap_auth_bind_user
info: Loading fact configured_ntp_servers
info: Caching catalog at /var/lib/puppet/state/localconfig.yaml
notice: Starting catalog run
notice: Finished catalog run in 0.11 seconds

localconfig.yaml doesn’t contain anything about /tmp/xen1.

Puppetmaster says:

info: Expiring the node cache of xen1.internal
info: Not using expired node for xen1.internal from cache; expired at Thu Jul 31 13:26:12 +0200 2008
info: Caching node for xen1.internal
debug: Creating default schedules
debug: /Settings[/etc/puppet/puppet.conf]/Settings[puppetmasterd]/File[/etc/puppet/manifests/site.pp]: Autorequiring File[/etc/puppet/manifests]
debug: Finishing transaction 23539572950260 with 0 changes
debug: Scope(Node[xen1.internal]): Collected 1 Deftest resource in 0.01 seconds
warning: Scope(Deftest[/tmp/xen1]): DEFTEST xen1.internal on /tmp/xen1
debug: Scope(Node[xen1.internal]): Collected 0 Deftest resources in 0.00 seconds
info: Stored catalog for xen1.internal in 0.35 seconds
notice: Compiled catalog for xen1.internal in 0.50 seconds

Which proved the puppetmaster has seen the collected resource. As the storeconfig database shows too:

mysql> select * from resources where host_id=79 and exported is not null;
+-------+-----------+---------+---------+----------------+----------+------+---------------------+
| id    | title     | restype | host_id | source_file_id | exported | line | updated_at          |
+-------+-----------+---------+---------+----------------+----------+------+---------------------+
| 51638 | /tmp/xen1 | File    |      79 |              1 |        1 |   12 | 2008-07-31 13:27:12 |
| 51639 | /tmp/xen1 | Deftest |      79 |              1 |        1 |   23 | 2008-07-31 13:27:12 |
+-------+-----------+---------+---------+----------------+----------+------+---------------------+
2 rows in set (0.01 sec)

Let’s play with xen2.internal now:

info: Loading fact configured_ntp_servers
info: Loading fact ldap_auth_bind_user
info: Loading fact netmask
info: Retrieving plugins
info: Loading fact configured_ntp_servers
info: Loading fact ldap_auth_bind_user
info: Loading fact netmask
info: Caching catalog at /var/lib/puppet/state/localconfig.yaml
notice: Starting catalog run
warning: //Node[xen2.internal]/Deftest[/tmp/xen1]/File[/tmp/xen1]/checksum: File /tmp/xen1 does not exist -- cannot checksum
notice: //Node[xen2.internal]/Deftest[/tmp/xen1]/File[/tmp/xen1]/checksum: defined 'checksum' as '{md5}d9b5059238064ee18522f48e51730c21'
notice: //Node[xen2.internal]/Deftest[/tmp/xen1]/File[/tmp/xen1]/content: created file with contents {md5}b23295cbdd57c2f30132fb656a6ddb17
notice: Finished catalog run in 0.41 seconds

The file /tmp/xen1 has been created (ie it exported the resource), but not the /tmp/xen2 file.

And the puppetmaster says:

info: Expiring the node cache of xen2.internal
info: Not using expired node for xen2.internal from cache; expired at Thu Jul 31 13:29:53 +0200 2008
info: Caching node for xen2.internal
debug: Scope(Node[xen2.internal]): Collected 2 Deftest resources in 0.01 seconds
warning: Scope(Deftest[/tmp/xen1]): DEFTEST xen2.internal on /tmp/xen1
warning: Scope(Deftest[/tmp/xen2]): DEFTEST xen2.internal on /tmp/xen2
debug: Scope(Node[xen2.internal]): Collected 0 Deftest resources in 0.00 seconds
info: Stored catalog for xen2.internal in 0.36 seconds
notice: Compiled catalog for xen2.internal in 0.40 seconds

Definitely, the puppetmaster knows both exported resources.

The database now contains:

+-------+-----------+---------+---------+----------------+----------+------+---------------------+
| id    | title     | restype | host_id | source_file_id | exported | line | updated_at          |
+-------+-----------+---------+---------+----------------+----------+------+---------------------+
| 51638 | /tmp/xen1 | File    |      79 |              1 |        1 |   12 | 2008-07-31 13:27:12 |
| 51639 | /tmp/xen1 | Deftest |      79 |              1 |        1 |   23 | 2008-07-31 13:27:12 |
| 51642 | /tmp/xen2 | File    |      80 |              1 |        1 |   12 | 2008-07-31 13:30:53 |
| 51643 | /tmp/xen2 | Deftest |      80 |              1 |        1 |   33 | 2008-07-31 13:30:53 |
+-------+-----------+---------+---------+----------------+----------+------+---------------------+

Back to xen1, now puppet can see the /tmp/xen2 file:

info: Loading fact netmask
info: Loading fact ldap_auth_bind_user
info: Loading fact configured_ntp_servers
info: Retrieving plugins
info: Loading fact netmask
info: Loading fact ldap_auth_bind_user
info: Loading fact configured_ntp_servers
info: Caching catalog at /var/lib/puppet/state/localconfig.yaml
notice: Starting catalog run
warning: //Node[xen1.internal]/Deftest[/tmp/xen2]/File[/tmp/xen2]/checksum: File /tmp/xen2 does not exist -- cannot checksum
notice: //Node[xen1.internal]/Deftest[/tmp/xen2]/File[/tmp/xen2]/checksum: defined 'checksum' as '{md5}2eba568f285d93e679f9d362dcf049b7'
notice: //Node[xen1.internal]/Deftest[/tmp/xen2]/File[/tmp/xen2]/content: created file with contents {md5}572ac7bb156d4895b43e08ccc8ef8076
notice: Finished catalog run in 0.08 seconds

Conclusion: it seems that each node cannot collect its own “defined” resources, but can still see the effects of the other nodes defined exported resources.

Note1: if I use only file{} instead of defines in the manifest, it works fine.

Note2: it used to work fine at least in 0.23.x, but the collection should have been written with the underlying type of the define (ie file{} instead of Deftest). I’m not sure at which version it started to not work, I noticed it first on 0.24.4.

Note3: I noticed the issue on a more complex sample, which had another outcome (basically an already defined resource error). I tried to simplify the failing testcase but lost the hability to generate this error message, and discovered this issue. I think solving this bug will solve my original issue. Please see: http://groups.google.com/group/puppet-users/browse_thread/thread/d04dccd244340581

site.pp - non working exported/collected resource. (402 Bytes) Brice Figureau, 07/31/2008 01:46 pm

fix-1472.patch - Temptative fix (618 Bytes) Brice Figureau, 08/01/2008 12:34 pm

History

Updated by Luke Kanies over 3 years ago

  • Status changed from Unreviewed to Needs More Information

Does this work if you don’t use the tags as a selection criterion?

That is, is it the use of tags that’s broken, or the collection itself?

I think it’s the tags, but I’d like confirmation.

Updated by Brice Figureau over 3 years ago

luke wrote:

Does this work if you don’t use the tags as a selection criterion?

That is, is it the use of tags that’s broken, or the collection itself?

I think it’s the tags, but I’d like confirmation.

No it’s not the tag. The following manifest doesn’t work either:

# site.pp
filebucket { server:
        server => "puppet"
}


define deftest ()
{
        warning("DEFTEST ${fqdn} on ${name}")
        file { "${name}":
                content => "${fqdn}"
        }
}


node "xen1.internal" {
  $part = "xen1"


        @@deftest {
                "/tmp/${part}":
        }
        Deftest <<| |>>
}

node "xen2.internal" {
  $part = "xen2"

        @@deftest {
                "/tmp/${part}":
        }
        Deftest <<| |>>
}

i.e. no /tmp/xen1 on xen1, a /tmp/xen1 on xen2, then a /tmp/xen2 on xen1. I modified puppetd to print the catalog YAML it receives to see if the problem was in the puppetmaster or puppetd itself (sorry I’m pretty new at ruby and puppet internals at the same time :–)) and definitely on the 3rd run (ie the run back on xen1) it produces:

!ruby/object:Puppet::TransBucket
name: :main
classes:
- xen1.internal
type: Class
children:
- !ruby/object:Puppet::TransBucket
  name: xen1.internal
  type: Node
  children:
  - !ruby/object:Puppet::TransBucket
    name: /tmp/xen2
    type: Deftest
    children:
    - !ruby/object:Puppet::TransObject
      line: 12
      name: /tmp/xen2
      type: file
      tags:
      - file
      - deftest
      - node
      - xen1.internal
      - class
      - main
      file: /etc/puppet/manifests/site.pp
      params:
        tag: test
        content: xen1.internal
  - !ruby/object:Puppet::TransBucket
    name: /tmp/xen1
    type: Deftest
    children: []

- !ruby/object:Puppet::TransObject
  line: 4
  name: server
  type: filebucket
  tags:
  - filebucket
  - server
  - class
  - main
  file: /etc/puppet/manifests/site.pp
  params:
    server: puppet

So I definitely get both resources, but only one is evaluated. Right now I’m trying to print more information form Puppet::Parser::Compiler to see what is different between both resources but I don’t know how to print object values (ie ala perl Data::Dumper or PHP print_r) to see objects internals.

Any idea where I should dig to understand the issue?

Thanks,

Updated by Luke Kanies over 3 years ago

masterzen wrote:

So I definitely get both resources, but only one is evaluated.

If you’re getting both resources, it’s not a collection problem; in fact, if it’s in the yaml file, it’s definitely not a language or server problem at all.

Run puppetd with —evaltrace, maybe that will help clarify things.

Updated by Brice Figureau over 3 years ago

luke wrote:

masterzen wrote:

So I definitely get both resources, but only one is evaluated.

If you’re getting both resources, it’s not a collection problem; in fact, if it’s in the yaml file, it’s definitely not a language or server problem at all.

I’m sorry, I might not have phrased correctly my conclusion due to my misunderstanding of puppet internals. I meant that the yaml catalog sent to puppetd contains both definition (ie the deftest) but only one has the underlying file resource child. The other one is only an empty shell (ie see in my previous post for the child: []).

Run puppetd with —evaltrace, maybe that will help clarify things.

Which gives on xen1 (3rd run):

info: Caching catalog at /var/lib/puppet/state/localconfig.yaml
notice: Starting catalog run
info: /Schedule[weekly]: Evaluated in 0.00 seconds
info: //Filebucket[server]: Evaluated in 0.00 seconds
info: /Schedule[daily]: Evaluated in 0.00 seconds
info: /Schedule[hourly]: Evaluated in 0.00 seconds
info: /Filebucket[puppet]: Evaluated in 0.00 seconds
info: /Schedule[puppet]: Evaluated in 0.00 seconds
info: /Schedule[never]: Evaluated in 0.00 seconds
info: //Node[xen1.internal]/Deftest[/tmp/xen2]/File[/tmp/xen2]: Evaluated in 0.03 seconds
info: /Schedule[monthly]: Evaluated in 0.00 seconds
notice: Finished catalog run in 0.16 seconds

And only one resource is evaluated (the one exported by xen2).

I still think the catalog creation/compilation in the puppetmaster is where the problem is. I’ll try to search in the code where the resource differs.

Updated by Brice Figureau over 3 years ago

luke wrote:

masterzen wrote:

So I definitely get both resources, but only one is evaluated.

If you’re getting both resources, it’s not a collection problem; in fact, if it’s in the yaml file, it’s definitely not a language or server problem at all.

Run puppetd with —evaltrace, maybe that will help clarify things.

I have now a better idea of what’s wrong.

When I run the first session (ie the first time puppetd is run on xen1.internal), the processing is the following: * in Puppet::Parser::Compiler::evaluate_generators the compiler checks for exported resources in evaluate_collections (there’s none at this time) the compiler checks for definition – got one (Deftest[“/tmp/xen1”]) it is then evaluated, which in turn produce the underlying File[“/tmp/xen1”] resource (in Puppet::Parser::AST::Definition::evaluate) this underlying resource is marked virtual because it inherits the scope of the define (which was virtual) since there are new resources in the catalog, a new run of evaluate_collections is scheduled evaluate_collections collects Deftest[/tmp/xen1] and sets its virtual param to false because it matches the collection criterions (type and such) ** unfortunately, it doesn’t collects the underlying defined resources (the File[/tmp/xen1]) which remains virtual. The collection doesn’t happen because this resource (although exported) doesn’t match the collection type (which is Deftest and not File). * Some time later, it is time to transform the catalog to transportable class for YAML export to the client. This part of the process only acts on non-virtual resource (which is normal behavior, you don’t want to send non realized resources to the client). * Thus my File[/tmp/xen1] resource is not sent and the puppetd client doesn’t evaluate it on its side.

I’m proposing a patch (against yesterday trunk) that solve the issue by analyzing during the collection the whole scope hierarchy to check if the current resource we are checking are descendant of a resource that would have matched the collection criterion.

This solves my current issue but I’m not sure if that’s the proper fix. I’m open to other suggestions :–)

Updated by Brice Figureau over 3 years ago

masterzen wrote:

I’m proposing a patch (against yesterday trunk) that solve the issue by analyzing during the collection the whole scope hierarchy to check if the current resource we are checking are descendant of a resource that would have matched the collection criterion.

This solves my current issue but I’m not sure if that’s the proper fix. I’m open to other suggestions :–)

I added the patch in the tickets/1472 branch in my github repository (masterzen/puppet). Direct access to the commit: http://github.com/masterzen/puppet/commit/9035d4b6dabe91ea2288f915497ad8d184368df8 Access to the branch: http://github.com/masterzen/puppet/commits/tickets/1472

Updated by James Turnbull over 3 years ago

  • Status changed from Needs More Information to Needs Decision
  • Assignee set to Luke Kanies

Updated by Luke Kanies over 3 years ago

  • Status changed from Needs Decision to Code Insufficient

Hmm, if you’re right about what’s happening (sorry, don’t have time to reproduce), then I think the bug is that the defined resource is being evaluated even though it’s virtual. Neither virtual nor exported defined resources should be evaluated at all, meaning that they should never produce contained virtual or exported resources. That is, stage 2 in your described process shouldn’t happen if the definition is virtual.

Line 271 of parser/compiler.rb explicitly skips virtual (and thus also exported) defined resource types.

Any other ideas what might be happening.

Updated by Brice Figureau over 3 years ago

luke wrote:

Hmm, if you’re right about what’s happening (sorry, don’t have time to reproduce), then I think the bug is that the defined resource is being evaluated even though it’s virtual. Neither virtual nor exported defined resources should be evaluated at all, meaning that they should never produce contained virtual or exported resources. That is, stage 2 in your described process shouldn’t happen if the definition is virtual.

No, that’s not exactly the issue. The issue is that collecting a define should collect underlying resources, which is not that happens (see below).

I just checked again, with the following minimal site.pp:

define deftest ()
{
   warning("DEFTEST ${fqdn} on ${name}")
   file { "/tmp/${name}":
     content => "test" 
   }
}

node "pouet.internal" {
  @@deftest {
    "pouet":
  }
  Deftest <<| |>>
}

I think you’d agree that, puppet should evaluate the deftest[pouet] resource on the node “pouet.internal” and issue the warning and create the /tmp/pouet file. That is not what happens (ie the warning is issued, but the file is not created).

I instrumented (ie lots of puts) and here is what happens:

  1. The node is evaluated:
Compiler::add_resource Node[pouet.internal]
resource is now Node[pouet.internal] exp: 
Resource::evaluate Node[pouet.internal]
Resource::evaluating a definedtype Node[pouet.internal]
  1. It gives the Deftest[pouet] virtual,exported resource:
Resource::initialize for pouet
resource::initialize set option type =  deftest
resource::initialize set option virtual =  true
resource::initialize set option scope =  Scope(Node[pouet.internal])
resource::initialize set option source =  pouet.internal
resource::initialize set option title =  pouet
resource::initialize set option params =  
resource::initialize set option exported =  true
Resource::exported=  true
Resource::exported=  force virtual and exported to true
resource::initialize set option line =  12
resource::initialize set option file =  /tmp/test/manifests/site.pp
  1. the deftest[pouet] resource is added to the catalog

  2. then, to the current collections

  3. the collections are then evaluated (first pass of compiler::evaluate_generators):

Compiler::evaluate_generators pass: 0
Compiler::evaluate_collections (entering)
Compiler::evaluate_collections collections not empty
Compiler::evaluate_collections for #
  1. Since deftest[pouet] is exported and virtual and matches the current collect selector, it is selected for collection:
Collector::collect_exported (entering)
Collector::collect_virtual for exported: true
Collector::collect_virtual can Deftest[pouet] be collected? true
Collector::collect_virtual can Node[pouet.internal] be collected? false
Collector::collect_virtual can Class[main] be collected? false
Collector::collect_exported resources Deftestcommit:pouet]
debug: Scope(Node[pouet.internal): Collected 1 Deftest resource in 0.01 seconds
  1. Then, back to evaluate_generators, evaluate_definitions is called and tries to evaluate deftest[pouet] which is not virtual anymore (because we collected it):
Compiler::evaluate_collectionse # collection evaluation found sth
Compiler::evaluate_collections (leaving)
Compiler::evaluate_generators after eval_collections done? false
Compiler::evaluate_definitions for Deftest[pouet]
Compiler::evaluate_definitions for Deftest[pouet], not virtual
  1. the deftest underlying resources are then evaluated and created as it should:
Resource::evaluate Deftest[pouet]
Resource::evaluating a definedtype Deftest[pouet]
evaluate_code for Deftest[pouet]
warning: Scope(Deftest[pouet]): DEFTEST arsenic.internal on pouet
Resource::initialize for /tmp/pouet
resource::initialize set option type =  file
resource::initialize set option virtual =  true
resource::initialize set option scope =  Scope(Deftest[pouet])
resource::initialize set option source =  deftest
resource::initialize set option title =  /tmp/pouet
resource::initialize set option params =  content => test
resource::initialize set option exported =  true
Resource::exported=  true
Resource::exported=  force virtual and exported to true
resource::initialize set option line =  6
resource::initialize set option file =  /tmp/test/manifests/site.pp
Compiler::add_resource File[/tmp/pouet]

You can see that File[/tmp/pouet] is now added to the catalog, which is right.

  1. Then we’re going to have another pass in Compiler::evaluate_generators to collect/evaluate resources or define that could have been created in the previous pass:
Compiler::evaluate_generators after evaluate_definitions done? false
Compiler::evaluate_generators pass: 1
Compiler::evaluate_collections (entering)
Compiler::evaluate_collections collections not empty
Compiler::evaluate_collections for #
  1. Guess what? There are some resources to collect:
Collector::collect_exported (entering)
Collector::collect_virtual for exported: true
Collector::collect_virtual can Deftest[pouet] be collected? true
Collector::collect_virtual can Node[pouet.internal] be collected? false
Collector::collect_virtual can File[/tmp/pouet] be collected? false
Collector::collect_virtual can Class[main] be collected? false
Collector::collect_exported resources 
debug: Scope(Node[pouet.internal]): Collected 0 Deftest resources in 0.00 seconds

Wait!! Something is wrong, it didn’t collect the File[/tmp/pouet], so this resource won’t ever be evaluated. Why is that: simply the resource type (File) doesn’t match the collected type (Deftest).

That’s why my patch goes beyond that to check that there is a parent defined resource that could match the type of the collected resource.

Hope that helps you understand the root issue.

Updated by Luke Kanies over 3 years ago

  • Target version set to 0.24.6

Updated by Luke Kanies over 3 years ago

  • Status changed from Code Insufficient to Ready For Checkin
  • Assignee changed from Luke Kanies to James Turnbull

Fixed in the [tickets/0.24.x/1472] branch in my repo.

Updated by James Turnbull over 3 years ago

  • Status changed from Ready For Checkin to Closed

Pushed in commit:98e79f8b7dbbdcb29c91b6099569e180bd8267c7 in branch 0.24.x

Also available in: Atom PDF