Details
Description
The error message when promised line could not select region should identify the region that can not be selected.
For example, instead of error: The promised line insertion 'color=red' could not select an edit region in '/tmp/config', we should see error: The promised line insertion 'color=red' could not select region 'section 1' in '/tmp/config'
set_variable_values_ini(tab, sectionName)
bundle agent __main__ { vars: '_config[section 1][color]'string => "red"; '_config[section_2][something]' string => "else"; "sections" slist => getindices( _config ); files: "/tmp/config" create => 'true', edit_line => set_variable_values_ini( "$(this.namespace):$(this.bundle)._config", $(sections) ); reports: "/tmp/config" printfile => cat( $(this.promiser) ); } #+begin_src from_stdlib body select_region INI_section(x) # @brief Restrict the `edit_line` promise to the lines in section `[x]` # @param x The name of the section in an INI-like configuration file { select_start => "\[$(x)\]\s*"; select_end => "\[.*\]\s*"; @if minimum_version(3.10) select_end_match_eof => "true"; @endif } bundle edit_line set_variable_values_ini(tab, sectionName) # @brief Sets the RHS of configuration items in the file of the form # `LHS=RHS` # # If the line is commented out with `#`, it gets uncommented first. # Adds a new line if none exists. # # @param tab An associative array containing `tab[sectionName][LHS]="RHS"`. # The value is not changed when the `RHS` is "dontchange" # @param sectionName The section in the file within which values should be # modified # # **See also:** `manage_variable_values_ini()` { vars: "index" slist => getindices("$(tab)[$(sectionName)]"); # Be careful if the index string contains funny chars "cindex[$(index)]" string => canonify("$(index)"); classes: "edit_$(cindex[$(index)])" not => strcmp("$($(tab)[$(sectionName)][$(index)])","dontchange"), comment => "Create conditions to make changes"; field_edits: # If the line is there, but commented out, first uncomment it "#+\s*$(index)\s*=.*" select_region => INI_section(escape("$(sectionName)")), edit_field => col("\s*=\s*","1","$(index)","set"), if => and("_section_$(section_name)_present_reached", "edit_$(cindex[$(index)])"); # match a line starting like the key something "\s*$(index)\s*=.*" edit_field => col("\s*=\s*","2","$($(tab)[$(sectionName)][$(index)])","set"), select_region => INI_section(escape("$(sectionName)")), classes => results("bundle", "set_variable_values_ini_not_$(cindex[$(index)])"), if => and("_section_$(section_name)_present_reached", "edit_$(cindex[$(index)])"); insert_lines: "[$(sectionName)]" location => start, comment => "Insert lines", classes => results( "bundle", "_section_$(section_name)_present" ); "$(index)=$($(tab)[$(sectionName)][$(index)])" select_region => INI_section(escape("$(sectionName)")), if => "!(set_variable_values_ini_not_$(cindex[$(index)])_kept|set_variable_values_ini_not_$(cindex[$(index)])_repaired).edit_$(cindex[$(index)])"; } body edit_field col(split,col,newval,method) # @brief Edit tabluar data with comma-separated sub-values # @param split The separator that defines columns # @param col The (1-based) index of the value to change # @param newval The new value # @param method The method by which to edit the field { field_separator => "$(split)"; select_field => "$(col)"; value_separator => ","; field_value => "$(newval)"; field_operation => "$(method)"; extend_fields => "true"; allow_blank_fields => "true"; } body location start # @brief Editing occurs before the matched line { before_after => "before"; } body classes results(scope, class_prefix) # @brief Define classes prefixed with `class_prefix` and suffixed with # appropriate outcomes: _kept, _repaired, _not_kept, _error, _failed, # _denied, _timeout, _reached # # @param scope The scope in which the class should be defined (`bundle` or `namespace`) # @param class_prefix The prefix for the classes defined # # This body can be applied to any promise and sets global # (`namespace`) or local (`bundle`) classes based on its outcome. For # instance, with `class_prefix` set to `abc`: # # * if the promise is to change a file's owner to `nick` and the file # was already owned by `nick`, the classes `abc_reached` and # `abc_kept` will be set. # # * if the promise is to change a file's owner to `nick` and the file # was owned by `adam` and the change succeeded, the classes # `abc_reached` and `abc_repaired` will be set. # # This body is a simpler, more consistent version of the body # `scoped_classes_generic`, which see. The key difference is that # fewer classes are defined, and only for outcomes that we can know. # For example this body does not define "OK/not OK" outcome classes, # since a promise can be both kept and failed at the same time. # # It's important to understand that promises may do multiple things, # so a promise is not simply "OK" or "not OK." The best way to # understand what will happen when your specific promises get this # body is to test it in all the possible combinations. # # **Suffix Notes:** # # * `_reached` indicates the promise was tried. Any outcome will result # in a class with this suffix being defined. # # * `_kept` indicates some aspect of the promise was kept # # * `_repaired` indicates some aspect of the promise was repaired # # * `_not_kept` indicates some aspect of the promise was not kept. # error, failed, denied and timeout outcomes will result in a class # with this suffix being defined # # * `_error` indicates the promise repair encountered an error # # * `_failed` indicates the promise failed # # * `_denied` indicates the promise repair was denied # # * `_timeout` indicates the promise timed out # # **Example:** # # ```cf3 # bundle agent example # { # commands: # "/bin/true" # classes => results("bundle", "my_class_prefix"); # # reports: # my_class_prefix_kept:: # "My promise was kept"; # # my_class_prefix_repaired:: # "My promise was repaired"; # } # ``` # # **See also:** `scope`, `scoped_classes_generic`, `classes_generic` { scope => "$(scope)"; promise_kept => { "$(class_prefix)_reached", "$(class_prefix)_kept" }; promise_repaired => { "$(class_prefix)_reached", "$(class_prefix)_repaired" }; repair_failed => { "$(class_prefix)_reached", "$(class_prefix)_error", "$(class_prefix)_not_kept", "$(class_prefix)_failed" }; repair_denied => { "$(class_prefix)_reached", "$(class_prefix)_error", "$(class_prefix)_not_kept", "$(class_prefix)_denied" }; repair_timeout => { "$(class_prefix)_reached", "$(class_prefix)_error", "$(class_prefix)_not_kept", "$(class_prefix)_timeout" }; } body printfile cat(file) # @brief Report the contents of a file # @param file The full path of the file to report { file_to_print => "$(file)"; number_of_lines => "inf"; } #+end_src
A run with a starting empty config file
error: The promised line insertion 'color=red' could not select an edit region in '/tmp/config' error: The promised line insertion 'color=red' could not select an edit region in '/tmp/config' error: The promised line insertion 'color=red' could not select an edit region in '/tmp/config' info: Inserted the promised line '[section 1]' into '/tmp/config' info: insert_lines promise '[section 1]' repaired info: Edited file '/tmp/config' info: Prepended the promised line '[section_2]' to /tmp/config info: insert_lines promise '[section_2]' repaired info: Inserted the promised line 'something=else' into '/tmp/config' after locator info: insert_lines promise 'something=else' repaired info: Edited file '/tmp/config' R: /tmp/config R: [section_2] R: something=else R: [section 1]