Giter Club home page Giter Club logo

engine-core's Introduction

Freemarker CLI Wrapper

CLI wrapper for the freemarker template engine.

It is part of the broader hamlet devops framework.

engine-core's People

Contributors

dependabot[bot] avatar github-actions[bot] avatar kshychko avatar ml019 avatar roleyfoley avatar rossmurr4y avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

engine-core's Issues

YAML output helper function

Expected Behavior

I can create a generated document which is treated like a JSON/freemarker object syntax during template generation but converted to yaml on export

Current Behavior

JSON support only

Possible Solution

Add a helper function to the freemarker wrapper which will convert from JSON to YAML. This function can be executed by freemarker when creating a file. This could follow on from the work in #23

Context

Some tools ( Jenkins config as code ) only supports YAML based files for its definitions. In order to dynamically create some sections of the configuraton for a Jenkins master we need a way to generate a yaml file

Add Github release support for the freemarker wrapper build

Expected Behaviour

Builds of the freemarker wrapper are built and available from the releases section of this repository

Current Behaviour

Builds are created by the developer who creates the change and are included in the repo as binary blobs

Possible Solution

Add a Java build process as a github action which supports publishing releases to this repository. Builds and tests should run on all builds but releases are created on repo tags

Context

This would allow for us to automate the build and test of the freemarker wrapper inline with CI/CD convetions

Hide stack trace on stop exception

Expected Behaviour

When a stop exception is raised within templates the stop reason is logged but the stack trace isn't included

Current Behaviour

When a stop event is raised the full stack trace is included in the log

Possible Solution

When handling the stop exception only log the message provided if possible and don't include the stack trace log to the console

Context

The stop method is used to stop processing at a user level and our convention is mostly to use the stop event as part of a fatal log message. This means the stack trace won't really provide information that is useful in debugging an issue

<feature> CMDB layered file system

Background

I've been mulling over how we move our CMDB processing forward. A few things I wanted to achieve

  1. Increased speed of processing - avoid all the bash processing and assembly of json files
  2. Simple cmdb structure - avoid adding in extra directory levels to handle differing CMDB granularity
  3. Read and write - want to be able to not only read CMDBs, but potentially add documents to them as well.

CMDB Layered File System

I'm proposing we introduce the idea of a logical CMDB file system, much like the unix file system, where each repo is "mounted" at a particular base path. Further, multiple repos can be mounted at the same path (hence the idea of layers, borrowing from the docker layered image). So I can have separate config and infrastructure repos but mount them at the same point in the tree. Thus whether it is one or more than one repo, the logical tree is the same.

For speed, I'm proposing that the Freemarker wrapper implement the CMDB file system. This will permit directory scanning and file reading/writing as part of normal template processing by exposing CMDB access functions via Freemarker. This will also permit reading AND writing to the CMDB, should we want to use the Freemarker engine to generate new files and save these to the CMDB.

While initially we will support physical file system backed CMDBs, the use of a file system abstraction means adding other sources (e.g. database backed cmdbs) can easily be accommodated in the future.

.cmdb extensions

The .cmdb file will be extended to indicate any CMDBs that could be layered under the existing CMDB. To avoid overlaps, file lookups will be sought in the base CMDB first, then in the layered CMDBs in the order they are specified. (Overlaps can also be avoided by selection of non-overlapping base paths). Each layer entry will indicate the CMDB required by its name, and the base path in the CMDB file system at which it should be mounted. Paths can be absolute or relative. Relative paths are appended to the base path for the CMDB containing the layer. CMDBs will thus be nestable to arbitrary levels.

{
  "Layers" : [
    {
      "Name" : "appointments",
      "BasePath" : "/products/appointments"
    }
  ]
}

Freemarker switches

Freemarker with have new command line switches to support assembling the CMDB file system.

The "-g" switch will support specification of the mapping of CMDB names to physical paths on the file system. Multiple -g switches instances will be supported. It will accept one or more values with two formats will be accepted,

  • name=path
  • path, where path cannot contain an "=" character

The first form names and locates a single CMDB.

The second form identifies a directory whose subtree is scanned for .cmdb files, with the containing directory being treated as a CMDB whose name is that of the containing directory. It is expected that a top-level set of CMDB directories will be the most common layout used.

Note that where CMDB directories are clones of git repos, the CMDB name need not be the same as the repo name, with the management of repo cloning/updating being out of scope as far as Freemarker is concerned.

The "-c" switch will define the CMDBs to be processed. Multiple -c switch instances will be supported. It will accept one or more values, each of which is treated as a CMDB name. If the -c switch is not provided, all CMDBs found via the -g switch will be considered available for processing.

The "-b" switch will define the base CMDB considered to be at the root of the CMDB structure, so the CMDB with a path of "/". If the switch is not provided, all CMDBs considered for processing will be mounted at "/default".

The three switches allow a variety of arrangements;

  • for automation processing, the -g switch could specify the tenancy and product/environment level CMDBs, with the -b switch indicating the tenancy CMDB as the starting point.

  • for local processing, the -g switch could point to a local parent directory under which all repos for a tenant would be cloned. The -c switch would then indicate the product to be processed, and the -b default value used to select the tenant CMDB as the starting point.

Processing functions

Freemarker will offer several functions for manipulating the CMDB file system.

getCMDBTree

[#local candidates =
  getCMDBTree(
    path,
    {
      "Regex" : []
      "IgnoreDotDirectories" : true,
      "IgnoreDotFiles" : true,
      "IncludeCMDBInformation" : false
    }
  ) ]

where

  • path is the absolute starting point within the CMDB file system to be scanned
  • the second parameter is a hash of options
    • Regex is an array of regular expressions to match the filename (default .*)
    • IgnoreDotDirectories controls if directories starting with "." should be ignored (default true)
    • IgnoreDotFiles controls if files starting with "." should be ignored (default true)
    • IncludeCMDBInformation includes cmdb information in the results (default false)

Regex will be used for limiting file extensions or looking for specific elements in file paths e.g. .../asFile/.... The entries of the array are considered to be ORed, mainly to keep the regular expressions simple.

The Java regex library has been used and searches need to be explicitly anchored if required. If no "^" anchoris provided, then the path must match the beginning of any string before it is considered for regex analysis, and "." is inserted before the regex. If a "^" anchor is provided, then the path is ignored. If no "$" anchor is present, the "." is postpended to any provided regex.

It should return a list of objects, one for each file found, modelled along the lines of .get_optional_template...

{
  "File":
  "Path" : 
  "Filename" :
  "Extension" :
  "Contents" : 
  "ContentsAsJSON" :
  "Include" :
  "CMDB" : { 
    "Name" : <name from -g/-c/-b>,
    "BasePath" :  <absolute path to cmdb in CMDB File System>,
    "ContentsAsJSON": <contents of .cmdb file if present>
    "File" : <full physical path to file>
  }
}

File is the absolute path to the file.

Path, Filename and Extension (no dot) reflect the found file details. Filename includes the Extension but not the Path. Path does not include the filename.

Contents is a function that will return the file contents as a string in the same way as the current -v option works. If the contents can be converted to JSON, the ContentsAsJSON attribute will be present permitting direct access to the parsed result.

If the file is considered to be a Freemarker template (First line = [#ftl]), then there will also be an "Include" attribute present that works the same as its equivalent in .get_optional_template.

This function will form the basis of processing to assemble the in-memory structure to be processed via component templates.

getCMDBs

[#local cmdbs =
  getCMDBs(
    {
      "ActiveOnly" : false
    }
   ) ]

where

  • the first parameter is a hash of options
    • ActiveOnly limits listed CMDBs to those that are to be considered for processing (default false)

This function will return a list of the cmdbs known to the engine;

{
  "Name" : 
  "CMDBPath" :
  "FileSystemPath" :
  "Base" :
  "Active" :
  "ParentCMDB" :
  "ContentsAsJSON":

}

Name is the CMDB name.

CMDBPath is the mount point for the cmdb in the CMDB File system.

FileSystemPath is the physical location of the CMDB on disk.

Base is a boolean indicating whether the CMDB is considered the base CMDB. If this flag is true, then CMDBPath will always be "/".

Active is a boolean indicating whether the CMDB is to be considered for processing.

ParentCMDB is the name of the parent CMDB by which the CMDB was mounted. This means it was listed in an entry of the Layers array within the .cmdb file of the parent CMDB.

ContentsAsJSON is the contents of the .cmdb file if present.

<bug> Stack overflow in IP Calculation

We were just trying to use the IPAddress function that you added to the freemarker wrapper for codeontap a while ago. When we call it we seem to mostly get Stackoverflows.

Exception in thread "main" java.lang.StackOverflowError
        at java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:89)
        at java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:72)
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)

I had a quick look on the developers site for the package we use and looks like a new version is out with some specific improvements to prefix handling
https://github.com/seancfoley/IPAddress/releases/tag/v4.3.0
Would you mind having a look at updating our wrapper to use this version or the version 5 ( latest ) if we can?
The address range we were trying get subnets for was pretty big ( "10.0.0.0/8". "20") But I would think it wouldn’t be too much work for it to overflow

JSON Output formatting

Expected Behaviour

When using toCMDB I can specify if a JSON file should be pretty formatted or compressed

Current Behaviour

All file outputs are compressed JSON files on a single line

Possible Solution

Add an option to specify Formatted on the toCMDB option. When this is specified the JSON output will be formatted with each key on a new line with 2 space indenting. Similar to the JQ standard output

echo '{ "this" : {"that": "other"}}' | jq 
{
  "this": {
    "that": "other"
  }
}

Context

This helps with debugging and readability of the output provided by the engine

feat: Files/Directories only search optimisation

Expected Behaviour

It should be possible to request directories only or files only when querying the cmdb

Current Behaviour

Currently this can only be done post search by checking the "IsDirectory" attribute. IT would be more efficient to perform this filtering pre regex checking.

Possible Solution

  • Add IgnoreDirectories option to getCMDBTree() with a default of false which will not include directories when scanning the file system
  • Add IgnoreFiles option to getCMDBTree() with a default of false which will not include files when scanning the file system

Context

Searching for directories only is used when determining the cmdb structure. Searching for files only is common when analysing the files within the top level directories of the cmdb structure.

Your Environment

  • CMDB Version used:
  • Blueprint:

CMDB Initialisation corrupting the plugin file system

A call to initialiseCMDBFileSystem() following a call to initialisePluginFileSystem() will cause subsequent calls to getPluginTree() to fail even though the requested files are present.

The issue only occurs if there are CMDB directories to examine. This was triggered by use of the -g switch.

Expected Behavior

Calls to getPluginTree() should work correctly after a call to initialiseCMDBFileSystem().

Current Behavior

Calls to getPluginTree() return no files even though files are present on disk.

Possible Solution

Steps to Reproduce (for bugs)

  1. run the wrapper with -g set
  2. make a call to initialiseCMDBFileSystem()
  3. make a call to initialisePluginFileSystem()
  4. make a call to getPluginTree() for files known to be on disk

Context

Bug is preventing progress on dynamic loading of the CMDB

Your Environment

  • CMDB Version used: v2.0.1
  • Blueprint:

Constrain searches based on path

Where a path parameter is provided to getCMDBTree() and getPluginTree() and a regex being processed is not start anchored (i.e. the path is included in the effective regex), the search can be constrained to cmdbs whose base bath is included in the provided path (or conversely excluded from analysis if the cmdb base path doesn't match the regex).

When combined with #20, this should result in a significant improvement in performance even in the face of a large number of active cmdbs. While searches for marker files may be expensive, the engine logic can maximise the length of the paths provided for specific details such as blueprints.

NOTE: An option to implement this that would actually apply to start anchored regexs would be to parse the regex expression and determine the "static" part at the front representing a fixed path. If this was used instead of the path, then it would potentially be more specific by including any static starting path components of the provided regex, as well as handling provided regex which as start anchored. If this approach is relatively straightforward, then it should be implemented in preference to analysis solely based on the path parameter. However, if this proves complex, base the implementation on the provided path as it is likely to result in 80% of the benefit.

JSON schema support

Expected Behaviour

I would like to experiment with the use of JSON schema for validation within the engine. Not sure what will be feasible but I need a method I can call to execute the schema engine.

Current Behaviour

No mechanism to check JSON against a JSON schema within the hamlet engine.

Possible Solution

Add a validateJson(document, schema, options) method that takes a json document and a json schema document and returns the results from whatever is the easiest schema engine to integrate. Thinking ideally a Java implementation would be best but a shell out to a CLI tool would work as well.

The options is a placeholder object like we use in our other apis to allow future tweaking of the process. For now it would be an empty object.

I know there will be issues with things like relative references but to start I think the schema will be isolated.

In terms of what is returned, I'd suggest an object so we can gather whatever output the engine produces e.g.

{
  "Status" : <return code of engine - 0 = ok hopefully>,
  "Logs" : [
    "...line 1 of the schema output...",
    "...line 2 of the schema output..."
  ],
  "Result" : ...
}

The Result would only be present if validation succeeds and should include whatever defaults have been applied by the engine

This might need a bit of refinement as we go but it will allow me to experiment to see how hard it would be to convert our existing schema validation to a more industry standard approach.

Context

[BUG] Lambda unit template generation failure

Current Behaviour

Template generation is failing for lambda components with the following error:
22:08:54 FreeMarker template error:
22:08:54 For "?eval" left-hand operand: Expected a string or something automatically convertible to string (number, date or boolean), but this has evaluated to an extended_hash (wrapper: f.c.AddConcatExpression$ConcatenatedHashEx):
22:08:54 ==> settings [in template "shared/inputseeders/shared/id.ftl" at line 374, column 64]
22:08:54
22:08:54 ----
22:08:54 FTL stack trace ("~" means nesting-related):
22:08:54 - Failed at: #local compositeSettings = (settings!... [in template "shared/inputseeders/shared/id.ftl" in function "shared_configseeder_cmdb" at line 374, column 5]
22:08:54 - Reached through: @internalRefreshInputState [in template "inputdata/inputsource.ftl" in function "getInputState" at line 448, column 9]
22:08:54 - Reached through: @writeStarterMessage writers=getComma... [in template "invokeEntrance.ftl" in function "invokeEntrancePass" at line 101, column 5]
22:08:54 ----

Expected Behaviour

Possible Solution

Steps to Reproduce

Your Environment

  • CMDB Version used: v2.0.1
  • Blueprint:

Investigate full bundled binaries for the hamlet-core engine

Expected Behaviour

When installing hamlet using pip install hamlet / hamlet engine install-engine no extra underlying requirements need to be setup for the java based freemarker wrapper

Current Behaviour

You need to ensure that java is installed whenever running hamlet commands

Possible Solution

Was Googling around and found that there are some tools to bundle a JRE with your Java application. That way it makes it an executable and you don't need to install a jre/jdk

This plugin for gradle seems to do it
https://badass-runtime-plugin.beryx.org/releases/latest/

And someone's covered the basics of it here
https://simply-how.com/custom-java-runtime

Context

The idea with this is to simplify the hamlet install process and make the hamlet tool independent from the rest of the actions performed within CI/CD tasks. At the moment we use the OS version of java which means that we need to ensure that things like jenkins agents are using the same version of java that we support

Case insensitive file path searching

To avoid case issues with file paths, add an option to control the case sensitivity of regex matches.

{
  "CaseSensitive" : false
}

By default, case sensitivity should be turned off in the regex engine. However, if case sensitive matches are desired, this option can be adjusted.

Note that the default above is not the current default, but seems a more pragmatic default.

General File Methods

Expected Behaviour

The hamlet engine can write to files that aren't part of a CMDB

Current Behaviour

The engine can only write to files which belong to a CMDB as part of the virtual file system

Possible Solution

Add non CMDB methods for the methods defined in
https://github.com/hamlet-io/engine-core/tree/master/freemarker-wrapper/src/main/java/io/hamlet/freemarkerwrapper/files/methods

They would accept similar parameters to the existing CMDB versions of these methods but would be based on an absolute OS file path.

Context

With hamlet-io/engine#1639 we found that writing logs to multiple destinations ( i.e. a console log and a JSON based log file ) was useful and provides an easy way to manage a machine readable log and a user readable log.

however these logs shouldn't be kept in a CMDB and are most likely going to be kept in tmp directories or the hamlet home directory, which aren't CMDBs

Hint to control filename glob used

At present, when the physical file tree is walked, a glob pattern is applied to the filename. It is mainly used for detecting cmdbs via the presence of .cmdb files. For normal processing, a value of "*" is used.

Introduce a new option to permit the caller to override the default value.

{
"FilenameGlob" : "tenant.*" (default *)
}

This value will be provided to th findr when scanning for files.

Add option controls for wildcard inclusion

At present, wildcards are inserted between the path and the regex if the regex isn't anchored at the start. As a result, where a fixed path is required, the path has to be bypassed but that means the search can't be constrained via the path. The same applies for end anchoring.

To permit more efficient searching using the path parameter, add the following options to getCMDBTree(0 and getPluginTree();

{
  "AddStartingWildcard" : (default : true),
  "AddEndingWildcard" : (default : true)
}

where

  • AddStartingWildcard prepends a ".*" wildcard to any regex that is not start anchored
  • AddEndingWildcard appends a ".*" wildcard to any regex that is not end anchored (along with the end anchor)

Both default to true to mirror current behaviour. These settings have no effect if the regex is start or end anchored respectively.

[BUG] Error calling getDomainObjects

Current Behaviour

Getting an error when trying to create a deploy template using the latest hamlet docker image

master└─ $ hamlet deploy create-deployments -l solution -u mgmt-rds
[!] Error Running Command

script: /home/hamlet/.hamlet/engine/engines/_global/shim/executor-bash/cli/createTemplate.sh -e unitlist -d update -p aws -f cf -i composite -o /home/hamlet/cmdb/cache/query/0126b95d5097ada1bcbd8157b3e87e83

##################################################
   stdout
##################################################

(Info) Generating outputs:
[*] entrance: unitlist | output: contract | subset: generationcontract

##################################################
   stderr
##################################################

Jun 21, 2021 10:55:20 AM freemarker.log._JULLoggerFactory$JULLogger error
SEVERE: Error executing FreeMarker template
FreeMarker template error:
Function "getDomainObjects" only accepts 1 parameters, but got 2.

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #return certificateObject + {"Domains...  [in template "common.ftl" in function "getCertificateObject" at line 730, column 5]
	- Reached through: @(.vars[macro]) occurrence=occurrence...  [in template "component.ftl" in function "invokeComponentMacro" at line 368, column 17]
	- Reached through: @(.vars[macro]) level=level  [in template "flow.ftl" in macro "processFlows" at line 16, column 13]
	- Reached through: @processFlows level=level framework=D...  [in template "shared/deploymentframeworks/default/output.ftl" in function "default_output_contract" at line 282, column 9]
	- Reached through: @generateOutput deploymentFramework=g...  [in template "shared/entrances/unitlist/entrance.ftl" in macro "shared_entrance_unitlist" at line 12, column 3]
	- Reached through: @(.vars[macro])  [in template "entrance.ftl" in macro "invokeEntranceMacro" at line 122, column 9]
	- Reached through: @invokeEntranceMacro type=entranceType  [in template "invokeEntrance.ftl" in function "invokeEntrancePass" at line 107, column 5]

Expected Behaviour

A template created successfully.

Possible Solution

Reproduced it in two different CMDBs with more than one Stem defined in the domains.json

Steps to Reproduce

  1. Use CMDB with a multiple Stem defined in the domains.json
  2. Run hamlet deploy create-deployment for any deployment unit

Your Environment

  • CMDB Version used: v1.3.2

bug: multiple plugin handling in mingw

Expected Behaviour

It should be possible to include multiple, semi-colon separated plugin dirs in GENERATION_PLUGIN_DIRS.

Current Behaviour

The wrapper reports the first plugin as not present and ignores the remainder.

Possible Solution

Steps to Reproduce (for bugs)

  1. export GENERATION_PLUGIN_DIRS="/c/repos/hamlet/engine-plugin-aws;/c/repos/hamlet/engine-plugin-cmdb"
  2. run create template
  3. Exception in thread "main" java.io.FileNotFoundException: \c\repos\hamlet\engine-plugin-aws does not exist.

Context

Developing new plugin under mingw

[BUG] Appending to a non-existent file throws a Java exception

Current Behaviour

If appending to a non-existent file, an exception is thrown.

Expected Behaviour

It would be preferrable if this followed shell redirection semantics and created the file is it doesn't exist.

Possible Solution

Fix underlying Java calls

Steps to Reproduce

  1. Call toCMDB with Append : true as an option for a non-existent file

IP Address Conversion Support

Some AWS resources, WAF only support specific CIDR masks:

AWS WAF supports /8, /16, /24, and /32 IPv4 address ranges and /16, /24, /32, /48, /56, /64, and /128 IPv6 address ranges. For more information about CIDR notation, see the Wikipedia entry Classless Inter-Domain Routing.

Most other resources support standard CIDR masking (i.e. any mask). Instead of restricting the codeontap configuration to only support the WAF masks we would like to have a function which can convert a given CIDR mask into an array of /32 IP addresses. WAF supports 10,000 IP Addresses in a single rule so it shouldn't be an issue to expand most subnets.

<fix> Only attempt to JSON parse files ending in .json

At present, parse exceptions are being thrown if a file is not in JSON format eg. pem files.

Need to

  • check the lower cased extension is "json" before attempting to parse the file
  • include the full physical file path when logging a JSON file parse exception

<feature> getPluginTree() function

There is a need to be able to scan parts of the provider configuration to determine which templates should then be dynamically loaded. To achieve this, add the getPluginTree() function.

It is modeled on the getCMDBTree, in that there is an "Plugin File System", where

  • each of the trees defined by -d switches are treated as a layer
  • each layer is mounted at "/"
  • the order of the -d switches defines the layering order
  • earlier layers hide later layers
[#local candidates =
  getPluginTree(
    path,
    {
      "Regex" : []
      "IgnoreDotDirectories" : true,
      "IgnoreDotFiles" : true,
      "IncludePluginInformation" : true
    }
  ) ]

where

  • path is the absolute starting point within the Plugin file system to be scanned
  • the second parameter is a hash of options, with the same meanings as getCMDBTree()

The result uses the same format as getCMDBTree(), but the Plugin Information is included.

A getPlugins() function will be the equivalent of getCMDBs().

[#local plugins =
  getPlugins(
    {}
  ) ]

where

  • the first parameter is a hash of options. None are defined at the moment

It will return an array of objects as follows

{
  "Name": "",
  "FileSystemPath": ""
}

Name is the plugin name detected based on the directory name.
FileSystemPath is the physical location of the plugin tree on disk.

<feature> Support multiple template directories

As part of adding dynamic loading to gen3 and supporting the idea of plugins, we need the ability for the template loader to look in more than one place.

  • add support for multiple "-d" switches to be provided (also support multiple paths after single -d switch)
  • order of -d switches (and optionally values after -d switch) determines order of template loaders

The default loader should still be based on a path of "/".

This will be GSGEN1.7.

Separate file system initialisation from usage

At present, the potentially expensive process of establishing what cmdbs/plugins are in a file system and their relationships to each other is being done as part of every call to search the file system.

To make this more efficient, introduce an explicit initialisation routine for the CMDB and Plugin file systems that must be called before any call to search the file system;

initialiseCMDBFileSystem()
initialisePluginFilesystem()

These routines can be called at any time to re-initialise the file systems, though at present there isn't a use case for this.

Use "hamlet" in place of "cot"

Expected Behavior

Change class names, error message etc to use the term "hamlet" rather than "cot".

Current Behavior

Existing names reflect the old frameowrk name.

Possible Solution

Context

As part of renaming the framework, the wrapper needs to be updated to reflect the new name.

YAML support in getCMDBTree() and getPluginTree()

If file contents can be parsed as YAML (triggered by a yml/yaml extension), convert yaml to json and offer via the ContentsAsJSON attribute

This will permit users to choose the format they prefer and even to mix and match.

Limiting directory tree recursion for getCMDBTree() and getPluginTree()

In order to improve the performance of these functions, this issue introduces ways in which the call can provide assistance in avoiding unnecessary recursions into subdirectories.

Control Recursion Depth

{
  "MinDepth" : n,  (optional)
  "MaxDepth" : m (optional)
}

These options control the range of subdirectories that should be checked for matching regex expressions. They are relative to the provided path, with the values starting at 1 for the directory pointed to by the provided path. Directories with a depth less than MinDepth are not considered in the search. The same goes for those whose depth is greater than MaxDepth.

A simple example where this options are useful is determining the directories at the top of a tree. By setting the MaxDepth to 1, only files/directories in the path directory will be considered for regex matching.

Control Recursion On Match

Where a search is attempting to find marker files, it is possible to ignore any subtrees below the found files. It is also possible to stop altogether on the first match, for instance when attempting to work out the directory structure of a cmdb.

{
  "StopAfterFirstMatch" : false,
  "IgnoreSubtreeAfterMatch" : false
}

where

  • StopAfterFirstMatch will return at most one result
  • IgnoreDescendantsAfterMatch will not recurse into any subdirectories under the one in which at least one match occurred.

Optimise Search Starting Point

Where a path is provided and a CMDB is found to be included under the path, searching of directories in the CMDB should start at the provided path, not the base path of the CMDB.

For example, if the base path for a CMDB is /products/widget and the provided path is /products/widget/path/in/cmdb then matching of files should start in the path/in/cmdb relative path within the cmdb, not at /products/widget.

stdout and stderr functions for freemarker wrapper

Expected Behaviour

A function can be called from within the freemarker engine to write output either the stdout or stderr console pipes.

Current Behaviour

We currently rely on output files generated by the engine to provide feedback to users and the bash executor reads the output file and then sends sections of it to stdout and stderr

Possible Solution

Add functions to the freemarker wrapper similar to the toCMDB function which will right to the console

Context

With the move to using the toCMDB function when a process fails within the engine that generates a fatal message or there is an issue with writing an output we won't be able to see the output or find results

Custom exceptions and return codes

Expected Behaviour

Exceptions generated by the engine are standardised and handled by the engine itself.

Current Behaviour

Error messages in the engine are currently classified by the executor once the exception is thrown by the engine

Possible Solution

Is it possible to create new exception freemarker method which allows for the setting the exit code, status code and along with an exception method. Currently the only exception method available is the freemarker builtin stop method, which only allows for string returns

Context

With hamlet-io/executor-bash#168 and hamlet-io/architectural-decision-log#5 we've identified the need for more complex error handling across hamlet in general. The current implementation relies on the executor to classify messages generated by the engine.

Rename getPlugins() to getPluginLayers()

Expected Behaviour

This function technically returns whatever is provided via the -d switches, which are used to provide the input to the template loader. They are combined into the layers of the plugin filesystem rather than being the plugins themselves. As an example, the plugin case has a single -d switch but can provide multiple plugins to the engine.

Thus this function should be renamed to getPluginLayers().

Current Behaviour

Currently getPlugins() actually returns the plugin filesystem layers.

Possible Solution

Rename the function.

Context

Ensure consistency of core functions.

Use .cmdb as a directory for cmdb related information

Expected Behaviour

Detection of a cmdb should be done on the basis of .cmdb being a file (legacy) or a directory.

If it is a directory, it should contain a config.json file with the current contents of the .cmdb file.

getCMDBs should return the path to the root of the cmdb, and the Contents should reflect the contents of either the .cmdb file or the .cmdb/config.json file.

Current Behaviour

.cmdb is expected to be a file.

Possible Solution

Code for either possibility.

Context

By changing this to a directory, other information such as cmdb migration history can also be captured.

[BUG] RunFreeMarker errors with JDK-11 (LTS)

Current Behaviour

$ hamlet -i whatif entrance invoke-entrance -e unitlist -o test/
results in the following (when using JDK-11)

(Info) Generating outputs:
WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.UnsupportedOperationException: No class provided, and an appropriate one cannot be found.
        at org.apache.logging.log4j.LogManager.callerClass(LogManager.java:576)
        at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:601)
        at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:588)
        at io.hamlet.freemarkerwrapper.RunFreeMarker.<clinit>(RunFreeMarker.java:42)
(Fatal) [!] hamlet unknown issue, code = 1
[!] Error Running Command

Expected Behaviour

$ hamlet -i whatif entrance invoke-entrance -e unitlist -o test/
results in the following (when using JDK-8)

(Info) Generating outputs:
[*] entrance: unitlist | output: contract | subset: generationcontract
[*] entrance: unitlist | output: contract | subset: managementcontract
(Info)  - updating unitlist-managementcontract.json
(Info)  - updating unitlist-generation-contract.json

Possible Solution

There is a note in https://stackoverflow.com/questions/53049346/is-log4j2-compatible-with-java-11 regarding maven-shade-plugin that may be useful

Your Environment

Switching between java-11-openjdk-amd64 and java-8-openjdk-amd64 on Ubuntu 20.0.4 reproduces error at will

<feature> Methods to update a CMDB

Expected Behavior

Additional methods should be available on the CMDB File System to

  • create a directory (equivalent of mkdir with -p as an option)
  • copy files between directories (equivalent of cp with -rp as an option)
  • remove files and directories (equivalent of rm with -rf as options)
  • create/append/overwrite a file ( equivalent of output redirection > and >>)

Any cache information will optionally be synchronised with these changes.

The file path will use the CMDB File System file path, and any new file/directory will be created

  • in the CMDB that matches the longest portion of the desired file path, and if more than one candidate ...
  • the highest CMDB in the CMDB hierarchy.

Current Behavior

The getCMDBTree() function currently only offers read access to CMDB files and directories. This change will round out its file support.

Possible Solution

The extra functionality could be covered by four new CMDB related functions as shown below. Where possible, the corresponding unix command behaviour should define the behaviour of these methods. This should permit a straightforward conversion of these requests to underlying operating system rquests.

mkdirCMDB - create directory

[#local result =
  mkdirCMDB(
    path,
    {
      "Parents" : false,
      "Synch" : true
    }
  ) ]

where

  • path is the path within the CMDB file system for the directory to be created
  • the last parameter is a hash of options
    • Parents is the equivalent of -p and will create any missing directories in the path (default false).
    • Synch will cause any cached information to be synchronised (default true)
  • the function returns the status of the operation consistent with unix status codes ( 0 = success).

cpCMDB - copy files/directories

[#local result =
  cpCMDB(
    from,
    to,
    {
      "Recurse" : false,
      "Preserve" : false,
      "Synch" : true
    }
  ) ]

where

  • from is the glob within the CMDB File System representing the files/directories that should be copied
  • to is the target within the CMDB File System representing where the files or directories should be copied
  • the last parameter is a hash of options
    • Recurse is the equivalent of -r and will recursively copy directories as well as files (default false).
    • Preserve is the equivalent of -p and will preserve the file attributes of existing files (default false).
    • Synch will cause any cached information to be synchronised (default true).
  • the function returns the status of the operation consistent with unix status codes ( 0 = success).

Note that this method will need to support copying of binary and text files.

rmCMDB - remove files/directories

[#local result =
  rmCMDB(
    path,
    {
      "Recurse" : false,
      "Force" : false,
      "Synch" : true
    }
  ) ]

where

  • path within the CMDB file system representing the file or directory to be deleted. Wildcards are not supported - use getCMDBTree to get a list of candidates and iterate over this instead
  • the last parameter is a hash of options
    • Recurse is the equivalent of -r and will recursively delete directories as well as files (default false).
    • Force is the equivalent of -f and will not error on non-existent files or directories (default false).
    • Synch will cause any cached information to be synchronised (default true).
  • the function returns the status of the operation consistent with unix status codes ( 0 = success).

toCMDB - write a file

[#local result =
  toCMDB(
    path,
    content,
    {
      "Append" : false,
      "Synch" : true,
      "Format" : "json"
    }
  ) ]

where

  • path is the path within the CMDB file system to the file to be created/updated
  • content is a freemarker object to be written to the file. The provided content should thus be serialised per the Format option, but arbitrary textual content can be written via use of a string as the content.
  • the last parameter is a hash of options
    • Append is the equivalent of >> and will cause the content to be appended to any existing file. (default false = overwrite).
    • Synch will cause any cached information to be synchronised (default true).
    • Format is one of "json", "yaml" or "yml". The content will be serialised in the format requested (default json) (see also #32)
  • the function returns the status of the operation consistent with unix status codes ( 0 = success).

fromCMDB - read directories/files

For consistency, getCMDBTree() could be renamed to fromCMDB, or an additional method added. Note that the return value would continue to be the results of the read, and NOT the status of the operation.

Context

This issue fleshes out hamlet-io/engine#1265, and is initially intended to support migration of the cmdb upgrade/cleanup process to be in the engine rather than in the executor.

It also opens the possibility of generating multiple outputs in a single invocation of the engine.

Your Environment

  • CMDB Version used: All

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.