Forum: CFEngine Help
Subject: Re: Deleting extra entries inside config that's built by edit_line(s)
Author: terok
Link to topic: https://cfengine.com/forum/read.php?3,25689,25810#msg-25810

Hi,

Took another approach to the issue, in which I could take advantage of the 
edit_defaults => empty. Now I just hit another snag, which just seems irrational
to me. 

My idea was to stage the config file first with a header line, which
contains the config blocks that I use.  With that I can workaround the issue of
removing entire config block section from the promises, and get that to trigger
re-staging of the config file. After the header would be verified (if_ok), then
i'd iterate through the "edit_line => set_config_values_w_delim_and_block"
promise. As a last step, i'd copy that from my staging place to the proper
place. This will work around the issues that someone would edit the actual
config by hand.

This actually worked, sort of.. The problem I hit next, was that
append_if_no_line gets into race condition with the 
set_config_values_w_delim_and_block. I do get the proper config out of this, 
but 
it's reset in each agent run and that's bad. For some reason the 
append_if_no_line, doesn't detect the line properly or is emptied all the time.

Here's an example of what 'sort of works', it just _always_ repairs the conf.
"Promises observed to be kept 76%, Promises repaired 24%, Promises not repaired 
0%"


body common control
{
    bundlesequence => { "block_conf" };
}

bundle agent block_conf

{
vars:

        "conf"  string => "/tmp/test.conf";
        "stage" string => "/tmp/stage_test.conf";

        debian::
                # List of config blocks we have.
                # Value is the  without []
                # Key is the array variable set below.
                "array_ref"       string =>       "USER";
                "array_ref"       string =>       "HOST";
                "array_ref"        string =>       "API";

        #  block
                "user"                 string => "randomuser",
                                policy => "free";
                "user"                string => "randompass",
                                policy => "free";

        #  block
                "host"                string => "randomhost",
                                policy => "free";
                "host"                    string => "randomport",
                                policy => "free";

        #  block
                "api"                       string => "/randomget",
                                policy => "free";
                "api"                     string => "/randompost",
                                policy => "free";

        # Open up the array_ref array.
                "array_ref_keys"        slist => getindices("array_ref");
                "array_ref_blocks"      slist => getvalues("array_ref");

        # Create string of the blocks in array_ref to use as a header in the 
config file.
                "all_avail_blocks"      string => join(", ","array_ref_blocks");

        # Setup the header for config files.
                "conf_header"           string => "#blocks in this conf: 
$(all_avail_blocks)";

files:


        "$(stage)"
                comment => "Stage the $(conf) config values to $(stage)",
                # Iterate over all available blocks and add the entries.
                edit_line => 
set_config_values_w_delim_and_block("block_conf.$(array_ref_keys)","=","$(array_ref[$(array_ref_keys)])"),
                ifvarclass => canonify("$(stage)_reset"),
                classes => if_repaired("$(stage)_set");

#       "$(conf)"
#               comment => "Copy the $(conf) from $(stage)",
#               copy_from => local_dcp("$(stage)");

        "$(stage)"
                comment => "re-stage the $(stage) for $(conf)",
                create => "true",
                edit_defaults => empty,
                edit_line => append_if_no_line("$(conf_header)"),
                classes => if_ok("$(stage)_reset");

}

bundle edit_line set_config_values_w_delim_and_block(v,delim,block)

# Sets the RHS of configuration items in the file of the form
# LHS delim RHS _after_ the given block. Also adds the block as such before 
entries:

# #
# [$(block)]
# lhs $(delim) rhs
# #

# If the line is commented out with #, it gets uncommented first.
# Adds a new line if none exists.

{
vars:
  "index" slist => getindices("$(v)");

  # Be careful if the index string contains funny chars
  "cindex[$(index)]" string => canonify("$(index)");

delete_lines:
   ".*"
     select_region => edit_in_between("^\[$(block)\]","^#\"),
     delete_select => 
not_starting_with("set_config_values_w_delim_and_block.index"),
     ifvarclass => "replace_attempted_$(cindex[$(index)])";

replace_patterns:
  # If the line is there, maybe commented out, uncomment and replace with
  # the correct value
   "^\s*($(index)\s+(?!$(delim)\s+$($(v)[$(index)])).*|# ?$(index)\s+.*)$"
     replace_with => value("$(index) $(delim) $($(v)[$(index)])"),
     select_region => edit_in_between("^\[$(block)\]","^#\"),
     classes => always("replace_attempted_$(cindex[$(index)])");

insert_lines:

   "#";
   "[$(block)]";
   "#";
   "$(index) $(delim) $($(v)[$(index)])"
   #insert_type => "preserve_block",
     select_region => edit_in_between("^\[$(block)\]","^#\"),
     ifvarclass => "replace_attempted_$(cindex[$(index)])";

}
# Select region between start and end patterns
body select_region edit_in_between(start, end) {
  select_start => "$(start)";
  select_end => "$(end)";
}

body delete_select not_starting_with(s)
{
        delete_if_not_startwith_from_list => { @(s) };
}

body copy_from local_dcp(from)                                                  
                                                
{
source      => "$(from)";
compare     => "digest";
}

body edit_defaults empty
{
empty_file_before_editing => "true";
edit_backup => "false";
max_file_size => "300000";
}

bundle edit_line append_if_no_line(str)
{
insert_lines:

 "$(str)"

     comment => "Append a line to the file if it doesn't already exist";        
                                                
}


body classes always(x)

# Define a class no matter what the outcome of the promise is

{
  promise_repaired => { "$(x)" };
  promise_kept => { "$(x)" };
  repair_failed => { "$(x)" };
  repair_denied => { "$(x)" };
  repair_timeout => { "$(x)" };
}

body replace_with value(x)
{
replace_value => "$(x)";
occurrences => "all";
}


body classes if_repaired(x)                                                     
                                                
{
promise_repaired => { "$(x)" };
}

body classes if_ok(x)                                                           
                                                
{
promise_repaired => { "$(x)" };
promise_kept => { "$(x)" };
}


This produces:


# cat /tmp/stage_test.conf
#blocks in this conf: USER, HOST, API
#

password = randompass
username = randomuser
#
#

hostname = randomhost
port = randomport
#
#

get = /randomget
post = /randompost
#


Meaby I'm missing some body option.. 
Any idea what's going on there and how to get those 2 promises to coexist 
nicely?

/Tero

_______________________________________________
Help-cfengine mailing list
Help-cfengine@cfengine.org
https://cfengine.org/mailman/listinfo/help-cfengine

Reply via email to