terraform get values for variables from yaml list - yaml

How can I read the values from the yaml file? I have no more ideas how to do that. This is a super simple example I know, but I still don't get it what the issue is. I need to do this with count not with for_each. The yaml decode part is done by terragrunt.
aws:
-
accounts: "dev"
id: "523134851043"
private_subnets:
eu-central-1a: "10.44.4.96/27"
eu-central-1b: "10.44.5.128/27"
eu-central-1c: "10.44.6.160/27"
-
accounts: "prod"
id: "098453041227"
private_subnets:
eu-central-1a: "10.44.7.0/27"
eu-central-1b: "10.44.8.32/27"
eu-central-1c: "10.44.9.64/27"
variable "aws" {
type = list(object({
accounts: string
id: string
private_subnets: list(object({
cidr: string
}))
}))
}
resource "aws_subnet" "private" {
count = length(var.aws.accounts[*].private_subnets)
availability_zone = element(keys(var.aws.accounts[*].private_subnets), count.index)
cidr_block = element(values(var.aws.accounts[*].private_subnets), count.index)
map_public_ip_on_launch = false
vpc_id = aws_vpc.this.id
It produces this error.
│ Error: Unsupported attribute
│
│ on test.tf line 40, in resource "aws_subnet" "private":
│ 40: count = length(var.aws.accounts[*].private_subnets)
│ ├────────────────
│ │ var.aws is a list of object, known only after apply
│
│ Can't access attributes on a list of objects. Did you mean to access
│ attribute "accounts" for a specific element of the list, or across all
│ elements of the list?
╵
╷
│ Error: Unsupported attribute
│
│ on test.tf line 42, in resource "aws_subnet" "private":
│ 42: availability_zone = element(keys(var.aws.accounts[*].private_subnets), count.index)
│ ├────────────────
│ │ var.aws is a list of object, known only after apply
│
│ Can't access attributes on a list of objects. Did you mean to access
│ attribute "accounts" for a specific element of the list, or across all
│ elements of the list?
╵
╷
│ Error: Unsupported attribute
│
│ on test.tf line 43, in resource "aws_subnet" "private":
│ 43: cidr_block = element(values(var.aws.accounts[*].private_subnets), count.index)
│ ├────────────────
│ │ var.aws is a list of object, known only after apply
│
│ Can't access attributes on a list of objects. Did you mean to access
│ attribute "accounts" for a specific element of the list, or across all
│ elements of the list?
╵
ERRO[0008] 1 error occurred:
* exit status 1

First, the variable type doesn't match your data and code you try to use. It should be:
variable "aws" {
type = list(object({
accounts: string
id: string
private_subnets: map(string)
}))
}
Then I don't understand why you couldn't use for_each. It normally leads code which is easier to understand and maintain, especially when refactoring. count will lead to resource recreations if the order of the data changes.
With for_each you would only need to make sure to use a unique index string, e.g. <az>/<cidr> in this case.
For example:
resource "aws_subnet" "private" {
# Build a map with unique index.
# It might be cleaner to extract this to a local variable.
for_each = merge([
for account in var.aws : {
for az, cidr in account.private_subnets :
"${az}/${cidr}" => { az = az, cidr = cidr }
}
]...)
availability_zone = each.value.az
cidr_block = each.value.cidr
# ...
}
If you really want to use count:
locals {
subnets = flatten([
for account in var.aws : [
for az, cidr in account.private_subnets : { az = az, cidr = cidr }
]
])
}
resource "aws_subnet" "private" {
count = length(local.subnets)
availability_zone = local.subnets[count.index].az
cidr_block = local.subnets[count.index].cidr
# ...
}

Related

Terraform template_file issue with a bash script

I am using template_file to pass a list from tfvars to my shell script, I do escape "$" in my tpl.sh script but then my script ends-up ignoring the values of my variables, how to go around this?
In below, the script ignores the values of $${i} and $${device_names[i]} because of the two dollar signs
for i in `seq 0 6`; do
block_device="/dev/nvme$${i}n1"
if [ -e $block_device ]; then
mapping_device="$${device_names[i]}"
I did attempt with eval and got an error in terraform
for i in `seq 0 6`; do
block_device="/dev/nvme" eval "${i}n1"
if [ -e $block_device ]; then
mapping_device=eval "${device_names[i]}"
It turns out that the above issue is that I was not calling the template correctly
But now I am trying to call the template in user_data but getting the below error, can someone pls advice?
locals {
cloud_config_config = <<-END
#cloud-config
${jsonencode({
write_files = [
{
path = "/usr/tmp/myscript.sh.tpl"
permissions = "0744"
owner = "root:root"
encoding = "b64"
#content = "${data.template_file.init.*.rendered}"
},
]
})}
END
myvars = ["var1","var2","var3"]
}
# Template_file
data "template_file" "init" {
template = "${file("${path.module}/script.sh.tpl")}"
vars = {
myvariables = join(" ", local.myvars)
}
}
# cloud-init to copy myscript to the instance "/usr/tmp/myscript.sh.tpl"
# I am not really sure how to move my rendered template into my instance
data "cloudinit_config" "userdata" {
gzip = false
base64_encode = false
part {
content_type = "text/cloud-config"
filename = "cloud-config.yaml"
content = local.cloud_config_config
}
depends_on = [ data.template_file.init ]
}
# User Data
user_data = data.cloudinit_config.userdata.rendered
This configuration throws this error and not really sure why
│ Error: Incorrect attribute value type
│
│ on main.tf line 31, in data "cloudinit_config" "userdata":
│ 31: content = data.template_file.init.*.rendered
│ ├────────────────
│ │ data.template_file.init is object with 5 attributes
│
│ Inappropriate value for attribute "content": string required.

Springboot cannot find templates

it is my controller
#Controller
#RequestMapping("/user")
public class UserController {
#GetMapping("/login")
public String login(){
System.out.println("hello");
return "user/login";
}
}
and it is my yaml file
spring:
thymeleaf:
check-template-location: true
prefix: classpath:/templates/
suffix: .html
cache: false
mode: HTML
encoding: utf-8
and it is my directory tree
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─example
│ │ │ └─facebook
│ │ │ ├─controller
│ │ │ ├─dao
│ │ │ ├─model
│ │ │ └─service
│ │ └─resources
│ │ ├─mapper
│ │ ├─static
│ │ └─templates
│ │ ├─hello
│ │ └─user
| └─ login.html
│ └─test
│ └─java
│ └─com
│ └─example
│ └─facebook
but... my project cant find login.html with 404 error, but print hello in console...
how can i fix it?
Since you mention that the controller prints the "hello" string, the mapping is working. Have you added thymeleaf dependency to your pom file?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

ManagedChannel not shut down properly

The error I put below, where a ManagedChannel was not shut down properly. I have read this issue, but couldn't find a solution: . So the question here is: what do I need to shut down and how?
I have basically completely copied the code from Google's docs. I will put the faulty class below the error as well. I have tried finding a "shutdown" method on all of the variables, but there aren't any, so I'm not sure what I should do.
2021-01-18 19:21:59.443 ERROR 1 --- [nio-8080-exec-8] i.g.i.ManagedChannelOrphanWrapper : *~*~*~ Channel ManagedChannelImpl{logId=157, target=homegraph.googleapis.com} was not shutdown properly!!! ~*~*~* │
│ Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true. │
│ java.lang.RuntimeException: ManagedChannel allocation site │
│ at io.grpc.internal.ManagedChannelOrphanWrapper$ManagedChannelReference.<init>(ManagedChannelOrphanWrapper.java:103) ~[grpc-core-1.15.1.jar!/:1.15.1] │
│ at io.grpc.internal.ManagedChannelOrphanWrapper.<init>(ManagedChannelOrphanWrapper.java:53) ~[grpc-core-1.15.1.jar!/:1.15.1] │
│ at io.grpc.internal.ManagedChannelOrphanWrapper.<init>(ManagedChannelOrphanWrapper.java:44) ~[grpc-core-1.15.1.jar!/:1.15.1] │
│ at io.grpc.internal.AbstractManagedChannelImplBuilder.build(AbstractManagedChannelImplBuilder.java:410) ~[grpc-core-1.15.1.jar!/:1.15.1] │
│ at com.google.actions.api.smarthome.SmartHomeApp.reportState(SmartHomeApp.kt:126) ~[actions-on-google-1.8.0.jar!/:na] │
│ at ordina.engie.googlehome.google.services.ReportStateService.reportState(ReportStateService.java:41) ~[classes!/:0.0.1-SNAPSHOT] │
│ at ordina.engie.googlehome.toon.controllers.ToonController.reportToGoogle(ToonController.java:27) ~[classes!/:0.0.1-SNAPSHOT]
The method where the error happens:
public void reportState(String requestId, String commonName, ThermostatInfo thermostatInfo) {
Value deviceStates =
Value.newBuilder()
.setStructValue(
Struct.newBuilder()
.putFields(HOME_RESPONSE_KEY_ONLINE, Value.newBuilder().setBoolValue(true).build())
.putFields(HOME_RESPONSE_KEY_MODE, Value.newBuilder().setStringValue(smartHomeHelperService.calculateThermostatMode(thermostatInfo)).build())
.putFields(HOME_RESPONSE_KEY_SETPOINT, Value.newBuilder().setNumberValue(thermostatInfo.getCurrentSetpoint()).build())
.putFields(HOME_RESPONSE_KEY_AMBIENT, Value.newBuilder().setNumberValue(thermostatInfo.getCurrentDisplayTemp()).build())
.build())
.build();
Struct requestStates = Struct.newBuilder().putFields(commonName, deviceStates).build();
HomeGraphApiServiceProto.ReportStateAndNotificationResponse response =
engieSmartHomeAppService.reportState(
HomeGraphApiServiceProto.ReportStateAndNotificationRequest.newBuilder()
.setRequestId(requestId)
.setAgentUserId(commonName)
.setPayload(
HomeGraphApiServiceProto.StateAndNotificationPayload.newBuilder()
.setDevices(
HomeGraphApiServiceProto.ReportStateAndNotificationDevice.newBuilder()
.setStates(requestStates)
.build())
.build())
.build());
}

How to detect in class where exactly the leak happens

Below is the leak description provided by leakcanary sdk. From below lines not able to detect that in which line i have to make changes to get code workable as description gives only class name not the particular code.
ApplicationLeak(className=com.pilotflyingj.plugin.a_android.ui.component.loyalty.RewardFragment, leakTrace=
┬
├─ android.view.inputmethod.InputMethodManager
│ Leaking: NO (InputMethodManager↓ is not leaking and a class is never leaking)
│ GC Root: System class
│ ↓ static InputMethodManager.sInstance
├─ android.view.inputmethod.InputMethodManager
│ Leaking: NO (DecorView↓ is not leaking and InputMethodManager is a singleton)
│ ↓ InputMethodManager.mNextServedView
├─ com.android.internal.policy.DecorView
│ Leaking: NO (View attached)
│ mContext instance of com.android.internal.policy.DecorContext, wrapping activity com.pilotflyingj.plugin.a_android.ui.component.main.MainFrameActivity with mDestroyed = false
│ Parent android.view.ViewRootImpl not a android.view.View
│ View#mParent is set
│ View#mAttachInfo is not null (view attached)
│ View.mWindowAttachCount = 1
│ ↓ DecorView.mAttachInfo
│ ~~~~~~~~~~~
├─ android.view.View$AttachInfo
│ Leaking: UNKNOWN
│ ↓ View$AttachInfo.mTreeObserver
│ ~~~~~~~~~~~~~
├─ android.view.ViewTreeObserver
│ Leaking: UNKNOWN
│ ↓ ViewTreeObserver.mOnScrollChangedListeners
│ ~~~~~~~~~~~~~~~~~~~~~~~~~
├─ android.view.ViewTreeObserver$CopyOnWriteArray
│ Leaking: UNKNOWN
│ ↓ ViewTreeObserver$CopyOnWriteArray.mData
│ ~~~~~
├─ java.util.ArrayList
│ Leaking: UNKNOWN
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[]
│ Leaking: UNKNOWN
│ ↓ array Object[].[2]
│ ~~~
├─ com.pilotflyingj.plugin.a_android.ui.component.loyalty.-$$Lambda$RewardFragment$-e5xQ8M432-uGOrhL3ajFrvOVQg
│ Leaking: UNKNOWN
│ ↓ -$$Lambda$RewardFragment$-e5xQ8M432-uGOrhL3ajFrvOVQg.f$0
│ ~~~
╰→ com.pilotflyingj.plugin.a_android.ui.component.loyalty.RewardFragment
​ Leaking: YES (Fragment#mFragmentManager is null and ObjectWatcher was watching this)
​ key = 11be43e3-0bcf-42fb-a087-025e52576844
​ watchDurationMillis = 8225
​ retainedDurationMillis = 3220
, retainedHeapByteSize=8730107)```
How to detech exact line of leak in class. Above is the leak description provided by leakcanary sdk.
Open the com.pilotflyingj.plugin.a_android.ui.component.loyalty.RewardFragment class and look for an implementation of ViewTreeObserver.OnScrollChangedListener.
The leak is happening because RewardFragment is setting a OnScrollChangedListener on ViewTreeObserver but not removing it when the fragment is destroyed.

How to create multiple entry points and use CommonsChunkPlugin in Ellixir webpack?

Is it possible to create multiple entry points (such as news.js, login.js) and use CommonsChunkPlugin (to nnt iinclude some common code, webpack code, etc. in all entry points) in Elixir with Webpack? Or do I need to create Webpack config file?
I created a simple webpack.config.js
var path = require("path");
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
context: path.join(__dirname, "resources/assets"),
entry: {
login: "./js/login.js",
post: "./js/post.js",
common: "./js/common.js"
},
output: {
path: path.join(__dirname, "public/js"),
filename: "[name].js"
},
plugins: [
new CommonsChunkPlugin({
filename: "common.js",
name: "common"
})
]
};
but what is the correct way to make Elixir use it?
Looks like it still needs a file path, that is mix.webpack() throws error about undefined.
mix.webpack('') fails during production build with this error:
Starting 'webpack'...
Hmm, not sure how to compress this type of file. Stick with CSS or JavaScript files!
mix.webpack(['post.js', 'login.js', 'common.js']) and mix.webpack('*.js') seems to work but looks like it was not intended to be used like this because destination output looks weird (though all.js is not created)
┌───────────────┬───────────────────────────────┬────────────────────────────────┬────────────────────┐
│ Task │ Summary │ Source Files │ Destination │
├───────────────┼───────────────────────────────┼────────────────────────────────┼────────────────────┤
│ mix.webpack() │ 1. Transforming ES2015 to ES5 │ resources\assets\js\common.js │ public\js\all.js │
│ │ 2. Writing Source Maps │ resources\assets\js\login.js │ │
│ │ 3. Applying Minification │ resources\assets\js\post.js │ │
│ │ 4. Saving to Destination │ │

Resources