Terragrunt path resolution - locally referenced modules - splitting into multiple environments - amazon-web-services

For business-related reasons, I am not able to split my infrastructure using versioned modules. As it is still beneficial to split the environments, and I would like to avoid the copy/paste root modules, which are basically just instantiation of child modules, over multiple directories, each representing its own environment, I would like to specify the source of root module in terragrunt.hcl
To make life easier, I have built a small part of the current infrastructure, just with a single module to speed up the development.
The current structure of the project looks like this:
├── config
│ ├── common.tfvars
│ └── terragrunt.hcl
├── environments
│ └── dev
├── infrastructure-modules
│ └── ecs
├── terraform-modules
│ ├── terraform-aws-ecr
All my infrastructure is described in infrastructure-modules/ecs/*.tf files, which are basically just instantiating child modules declared in terraform-modules/terraform-aws-*/.
With that, I can simply execute the terragrunt (terraform commands) from the infrastructure-modules/ecs directory.
To have a possibility to create the same environment in another account, I have introduced a new directory environments/dev/eu-central-1/ecs as shown on tree output from the root directory.
The environments/dev/eu-central-1/ecs, consists just of two files, terragrunt.hcl and common.tfvars.
I guess, that the usage of the common.tfvars is quite self-explanatory, where my terragrunt.hcl consists of remote_state {} and terraform {} blocks.
The important part of the terragrunt configuration file:
remote_state {}
terraform {
source = "../../../../infrastructure-modules/ecs"
{...}
}
Above I am basically referencing my root modules, declared in infrastructure-modules/ecs/*.tf. Where my root modules are instantiating child-modules declared in terraform-modules/terraform-aws-*/.
Child modules from infrastructure-modules/ecs/*.tf are instantianed like this:
module my_module {
source = "../../terraform-modules/terraform-aws-*"
{...}
}
In an ideal world, I would be able to execute terragrunt (terraform) commands from environments/dev/eu-central-1/ecs directory, but as I am using local (relative) paths, this is failing during the initialization of the modules, as the root module my_module loads the child module with following relative path:
module my_module {
source = "../../terraform-modules/terraform-aws-*"
{...}
}
This is causing a module instantiation in environments/dev/eu-central-1/ecs to fail as the relative path is different, based on parent module instantiation.
Initializing modules...
- my_module in
Error: Unreadable module directory
Unable to evaluate directory symlink: lstat ../../terraform-modules: no such
file or directory
So far, according to the documentation, path_relative_*, should be able to return the relative path between the path specified in its include block and the current terragrunt.hcl, but the problem here is that I am not having any include {} block(s) within my terragrunt.hcl files and thus this approach doesn't works. Symlinks are the last option.
EDIT
If I inspect the .terragrunt-cache/* on path environments/dev/eu-central-1/ecs I can confirm that all the "root" modules have been downloaded(copied over) into cache directory.
However, the module is being instantiated like this, and it tries to fetch the actual modules (Terraform modules) from directory two levels above.
module my_modules {
source = "../..//terraform-modules/terraform-aws-ecr"
So basically, I need to tell Terragrunt to download/fetch the modules from other path.
EDIT 2:
Inspecting .terragrunt-cache in the directory where I am running init shows, the the terraform-modules are never downloaded in the
terraform-infrastructure/environments/dev/eu-central-1/ecs/.terragrunt-cache/.
If I change my terraform-infrastructure/infrastructure-modules/ecs/ecr-repos.tf, from
module ecr_lz_ingestion {
source = "../../terraform-modules//terraform-aws-ecr"
{<MODULE_ARGUMENTS>}
}
}
to:
module ecr_lz_ingestion {
source = "../../../../../../../../terraform-modules//terraform-aws-ecr"
{<MODULE_ARGUMENTS>}
}
}
Terraform is able to initialize the child-modules as I have given a relative path to terraform-modules/ in the directory root, which is obviously a workaround.
Somehow I am expecting the Terragrunt to download both directories, terraform-modules and infrastructure-modules for the relative paths in module instantiation to work.

Based off the additional information you provided, I understand that this is your current directory structure:
terraform-infrastructure
├── config
│ ├── common.tfvars
│ └── terragrunt.hcl
├── environments
│ └── dev
│ └── eu-central-1
│ └── ecs
│ └── terragrunt.hcl
├── infrastructure-modules
│ └── ecs
├── terraform-modules
│ ├── terraform-aws-ecr
│
├── terragrunt.hcl
Notice the terragrunt.hcl in the parent directory that I added.
That terragrunt.hcl is considered the parent file and can include code that can be shared among other terragrunt files.
It can include something like this:
remote_state {}
In your eu-central-1/ecs folder, add the following to your terragrunt file:
include {
// searches up the directory tree from the current terragrunt.hcl file
// and returns the absolute path to the first terragrunt.hcl
path = find_in_parent_folders()
}
// using the path relative from the path stated in the include block
terraform {
source = "${path_relative_from_include()}//infrastructure-modules”
}
This should keep relative pathing intact when child modules are instantiating.
Edit:
From your GitHub issue, it would be best to either move the double slash to somewhere the modules share a common path with. Either that or to just consolidate your modules into a single folder.

Related

How to get unit tests (using CMake and GTest) to know where a folder of test data is?

I am using CMake and GTest to unit test a C++ program. One of my tests uses fopen() to open a file of test data.
I am struggling to figure out how to not get a "No such file or directory" error.
Directory Structure
├── CMakeLists.txt
├── build
├── src
│ └── myProgram.cxx
└── tests
├── CMakeLists.txt
├── data
│ ├── dataset1.txt
│ ├── dataset2.txt
│ ├── dataset3.txt
│ └── dataset4.txt
└── myProgramTests.cxx
Test Code
TEST(test, read_data_file) {
// Open test file
std::FILE *f = fopen("inputs/dataset1.txt", "r");
if (f == NULL){
perror ("Error opening file");
}
fclose(f);
}
This seems simple, but I can't figure out what to put here. I have tried "dataset1.txt", "inputs/dataset1.txt", "tests/inputs/dataset1.txt". What am I missing / is there a way for me into "include" these files via a line in CMakeLists.txt so I can just read them in with one of the strings I tried above?
Summary: How do I properly reference the location of files stored in a tests/data subdirectory within GTest?
Use ctest of cmake. Its add_test command has a useful property WORKING_DIRECTORY that are you looking for.
Paths that do not start with a / are relative to your current working directory, i.e the directory your shell is in when you run the tests.
For example, if your current working directory is the top-level directory of your project, then the relative path to dataset1.txt is tests/data/dataset1.txt

How can I avoid relative paths in #includes when building with Bazel

I'm struggling to understand the logic of how includes work in Bazel targets. I want my code to be modular, so I am trying to avoid #include statements with relative or long absolute paths.
Suppose I have the following workspace structure:
tree .
.
├── BUILD
├── is_binary_tree
│   ├── BUILD
│   └── is_binary_tree.cpp
├── lib
│   ├── BUILD
│   ├── graphs.cpp
│   └── graphs.h
└── WORKSPACE
I'm getting the following warning when trying to bazel build //is_binary_tree:is_binary_tree and I don't understand what it means :
WARNING: /is_binary_tree/BUILD:1:10:
in includes attribute of cc_binary rule
//is_binary_tree:is_binary_tree: '../lib' resolves to 'lib' not below
the relative path of its package 'is_binary_tree'. This will be an
error in the future
Why would ../lib resolve to lib. Lib should be in the parent directory of is_binary_tree, so from the standpoint of is_binary_tree it can be found at ../lib, isn't this right?
To get rid of the relative path and avoid having something like #include ../lib/graphs.h in is_binary_tree/is_binary_tree.cpp I added an includes attribute to my is_binary_tree target like so:
is_binary_tree/is_binary_tree.cpp
#include "graphs.h"
int main(){
return 0;
}
is_binary_tree/BUILD
cc_binary(
name="is_binary_tree",
srcs=["is_binary_tree.cpp"],
includes=["../lib"],
deps=["//lib:graphs"],
)
And I'm getting the aforementioned WARNING. What am I missing?
And more broadly, what is the best way to include dependencies without having long relative paths in #include statements ? (I want my code to be modular and not specific to a given Bazel workspace folder organization)
Thanks
That includes should go in //lib:graphs, so that anything which depends on it (has it in deps) uses it. lib/BUILD should look like this:
cc_library(
name = "graphs",
hdrs = ["graphs.h"],
srcs = ["graphs.cpp"],
includes = ["."],
visibility = ["//visibility:public"],
)
Then you drop includes from is_binary_tree and it should work.
In general, each Bazel target contains information about its files. It depends on other targets to use their files.
More broadly, Bazel defaults to #include paths relative to the base of the repository. That means you'd write #include "lib/graphs.h" in any file, whether that's is_binary_tree/is_binary_tree.cpp or x/y/z/foobar.cpp. That avoids collisions between graphics/constants.h and audio/constants.h, without using absolute paths.

How do I import a package from the local filesystem using dub?

I have a project that uses dub. I want to use an external file vendored into my project, as a dependency. How do I do this? I don't want to have it in my project's source/ dir. I don't want to add it as a dub managed dependency, but I do want to be able to just import xxx.
The package is this one: https://github.com/gianm/d-json , it does not use dub or have a dub.json project file.
Alternative thing: make a third_party directory, put the file in there, then add that to the sourcePaths in your dub config (you'll probably specify both ["third_party", "source"] since the default source will be overridden if you don't list it too.
Convert the package to dub by adding a dub.json file in the root, with the following contents: {"name": "jsonx"}. Create a source folder, and move jsonx.d into it.
Put the folder anywhere you want, e.g. top-level next to your own project.
Add the following dependency to your dub.json:
"dependencies": {
...
"jsonx": {"path": "../jsonx/"}
}
You can now import the package anywhere using import jsonx;.
In conclusion, if your app is in a dir called app, your tree should look like this:
.
├── app
│   ├── dub.json
│   └── source
│      └── myapp.d
└── jsonx
├── dub.json
└── source
└── jsonx.d

Sublime Linter with clang can't find header file in header

I'm using sublime text with the sublime linter plugin especially with clang.
When I open a folder, it use the root of the folder as a header location, so if I have
src
├── World
│   ├── Chunk.cpp
│   ├── Chunk.hpp
│   ├── World.cpp
│   └── World.hpp
└── main.cpp
In World.cpp I need to include "World/World.hpp".
But if in World.hpp I include Chunk.hpp the same way ("World/Chunk.hpp"), I get an error but
in World.hpp I have no error. Error are only in file I include that include other file.
I had the same issue. You need to tell clang where to look for the files, i.e. which directories you want to include. Go Preferences --> Package Settings --> SublimeLinter --> Settings and add a new section for clang++:
// SublimeLinter Settings - User
{
"linters":
{
"clang++": {
"I" : [
"${folder}/src",
"${file_path}",
]
}
}
}
In your case the two include directories will actually point to the same path but in general, the first version is to include your source directory (e.g. you have a unit test from a different folder open which accesses code from you src directory) and the second line includes the location of your current file.
If you need different directories, find some more variables you can use here.

Creating C++ library with CMake

I am trying to create a reasonable library structure and CMake file which would allow other CMake projects to easily include this library. I've found a lot of similar questions, however, none of them seems to address my exact problem.
My current structure is the following:
/*MyLibrary/
├── CMakeLists.txt (including lib subdirectories)
├── external/
│ └── googletest/
├── lib/
│ ├── common/
│ │ ├── CMakeList.txt (creates static lib common)
│ │ ├── include/common/*.h
│ │ └── src/*.cpp
│ ├── cipher/
│ │ ├── CMakeList.txt (creates static lib cipher)
│ │ ├── include/cipher/*.h
│ │ └── src/*.cpp
└── test/
├── main.cpp (code for executing google test)
├── CMakeLists.txt (creates unittest executable)
├── common/*Test.cpp
└── cipher/*Test.cpp
*/
Now I want to create a project with a similar directory structure but the problem occurs when I want to create a static library in that project with the same name as one of the static libraries in MyLibrary (common for example).
I was thinking of including the library into the project using add_subdirectory(external/MyLibrary) in the projects CMakeLists, but that fails because the names of the static libraries collide.
Even when I solve this issue by renaming the library (which I don't in fact believe is an elegant solution), I end up with googletest conflicts because both, the library and my project, are dependent on googletest.
Is there any way to solve this easily? I was thinking of 2 possibilities:
In the MyLibrary I would create one static library from all the sub-libraries called libMyLibrary.aand include only that library (preferred option, but I don't have a clue how to achieve it in CMake)
Forcing the users of the library to install it and don't include the library as a project submodule (last option I would go for)
Is there some other reasonable way to solve it which would make my library compatible with most CMake projects? If not, how can I achieve at least one of the mentioned options?
In short: how can I create a CMake file for a library so the library is easy to include in other CMake projects?
How can I deal with redundant dependencies such as googletest?
How can I create a CMake file for a library so the library is easy to include in other CMake projects?
There is no universal approach for that purpose.
By including your project into another one with add_subdirectory(), you "open" internals of your project to other one. Aside of advantage in having the library's target ready for linking, this approach has disadvantages too. Target's collisions, cached variables collisions will be your headache, among with some other problems.
If you want your project to be included into other with add_subdirectory, avoid using "generic" names for your targets. E.g., use <my_project_name>_common instead of common.
Some targets you cannot rename (like gtest). If those targets aren't really required for your project to work, create an option for disable them:
option(<my_project_name>_TESTING "Enable testing" ON)
if(<my_project_name>_TESTING)
add_subdirectory(external/googletest)
endif()
See also my answer to the similar question about targets' names collision.