Skip to content

Commit 82e1b2d

Browse files
adrianrioboclaude
andcommitted
feat(ibmcloud): add otelcol-contrib filelog collector to IBM Power and IBM Z
Installs and configures the OpenTelemetry Collector contrib distribution on both IBM Power (ppc64le) and IBM Z (s390x) instances when --otel-app-code, --otel-auth-token, and --otel-index are all provided. Collector configuration: - Three filelog receivers: syslog, secure/auth, and audit logs - Operators: move log.file.path to _sourceName, remove log.file.name, timestamp parsing for syslog/secure receivers - Splunk routing attributes per receiver: index, _sourceCategory, _sourceHost - Resource processor: appcode, arch (hardcoded per platform), auth token, and optional extra attributes via --otel-extra-attrs - Auth token isolated in a 0600 EnvironmentFile; config.yaml is 0640 - IBM Z user_data wrapped in MIME multipart for cloud-init compatibility Other fixes folded in: - Remove deprecated otelcol metrics.address field - Remove redhat.com from NO_PROXY - Harden waitForInstance: return error on timeout, use mCtx.Context() for cancellation, replace fmt.Println with logging.Infof - Extract shared NewSecurityGroupWithSSH and NewFloatingIP helpers into the network package; remove unused constants package - Fix SA4004 staticcheck: replace unconditionally-terminated loop in power.New() with explicit empty-check and index access Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 493b62a commit 82e1b2d

14 files changed

Lines changed: 674 additions & 179 deletions

File tree

.golangci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ run:
55
skip-dirs:
66
- vendor
77
- tools/vendor
8-
skip-files:
9-
- pkg/provider/ibmcloud/action/powervs/powervs.go
108
concurrency: 1
119

1210
linters:

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ CIRRUS_CLI ?= v0.165.2
1212
GITHUB_RUNNER ?= 2.334.0
1313
# renovate: datasource=gitlab-releases depName=gitlab-org/gitlab-runner
1414
GITLAB_RUNNER ?= 18.11.3
15+
# renovate: datasource=github-releases depName=open-telemetry/opentelemetry-collector-releases
16+
OTELCOL_VERSION ?= 0.151.0
1517

1618
# Go and compilation related variables
1719
GOPATH ?= $(shell go env GOPATH)
@@ -25,7 +27,9 @@ MODULEPATH = $(ORG)/mapt
2527
VERSION_VARIABLES := -X $(MODULEPATH)/pkg/manager/context.OCI=$(IMG) \
2628
-X $(MODULEPATH)/pkg/integrations/cirrus.version=$(CIRRUS_CLI) \
2729
-X $(MODULEPATH)/pkg/integrations/github.runnerVersion=$(GITHUB_RUNNER) \
28-
-X $(MODULEPATH)/pkg/integrations/gitlab.version=$(GITLAB_RUNNER)
30+
-X $(MODULEPATH)/pkg/integrations/gitlab.version=$(GITLAB_RUNNER) \
31+
-X $(MODULEPATH)/pkg/provider/ibmcloud/action/ibm-power.otelColVersion=$(OTELCOL_VERSION) \
32+
-X $(MODULEPATH)/pkg/provider/ibmcloud/action/ibm-z.otelColVersion=$(OTELCOL_VERSION)
2933
LDFLAGS := $(VERSION_VARIABLES) ${GO_EXTRA_LDFLAGS}
3034
GCFLAGS := all=-N -l
3135
GOOS := $(shell go env GOOS)

cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ func ibmPowerCreate() *cobra.Command {
5858
PIPrivateSubnetID: viper.GetString(params.PIPrivateSubnetID),
5959
WorkspaceID: viper.GetString(params.WorkspaceID),
6060
VPCPublicSubnetID: viper.GetString(params.VPCPublicSubnetID),
61+
OtelAppCode: viper.GetString(params.OtelAppCode),
62+
OtelAuthToken: viper.GetString(params.OtelAuthToken),
63+
OtelEndpoint: viper.GetString(params.OtelEndpoint),
64+
OtelIndex: viper.GetString(params.OtelIndex),
65+
OtelExtraAttrs: viper.GetStringMapString(params.OtelExtraAttrs),
6166
})
6267
},
6368
}
@@ -67,6 +72,11 @@ func ibmPowerCreate() *cobra.Command {
6772
flagSet.StringP(params.PIPrivateSubnetID, "", "", params.PIPrivateSubnetIDDesc)
6873
flagSet.StringP(params.WorkspaceID, "", "", params.WorkspaceIDDesc)
6974
flagSet.StringP(params.VPCPublicSubnetID, "", "", params.VPCPublicSubnetIDDesc)
75+
flagSet.StringP(params.OtelAppCode, "", "", params.OtelAppCodeDesc)
76+
flagSet.StringP(params.OtelAuthToken, "", "", params.OtelAuthTokenDesc)
77+
flagSet.StringP(params.OtelEndpoint, "", "https://otel-input.corp.redhat.com", params.OtelEndpointDesc)
78+
flagSet.StringP(params.OtelIndex, "", "", params.OtelIndexDesc)
79+
flagSet.StringToStringP(params.OtelExtraAttrs, "", nil, params.OtelExtraAttrsDesc)
7080
params.AddGHActionsFlags(flagSet)
7181
params.AddCirrusFlags(flagSet)
7282
c.PersistentFlags().AddFlagSet(flagSet)

cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,24 @@ func ibmZCreate() *cobra.Command {
5555
Tags: viper.GetStringMapString(params.Tags),
5656
},
5757
&ibmz.ZArgs{
58-
SubnetID: viper.GetString(params.SubnetID),
58+
SubnetID: viper.GetString(params.SubnetID),
59+
OtelAppCode: viper.GetString(params.OtelAppCode),
60+
OtelAuthToken: viper.GetString(params.OtelAuthToken),
61+
OtelEndpoint: viper.GetString(params.OtelEndpoint),
62+
OtelIndex: viper.GetString(params.OtelIndex),
63+
OtelExtraAttrs: viper.GetStringMapString(params.OtelExtraAttrs),
5964
})
6065
},
6166
}
6267
flagSet := pflag.NewFlagSet(params.CreateCmdName, pflag.ExitOnError)
6368
flagSet.StringP(params.ConnectionDetailsOutput, "", "", params.ConnectionDetailsOutputDesc)
6469
flagSet.StringToStringP(params.Tags, "", nil, params.TagsDesc)
6570
flagSet.StringP(params.SubnetID, "", "", params.SubnetIDDesc)
71+
flagSet.StringP(params.OtelAppCode, "", "", params.OtelAppCodeDesc)
72+
flagSet.StringP(params.OtelAuthToken, "", "", params.OtelAuthTokenDesc)
73+
flagSet.StringP(params.OtelEndpoint, "", "https://otel-input.corp.redhat.com", params.OtelEndpointDesc)
74+
flagSet.StringP(params.OtelIndex, "", "", params.OtelIndexDesc)
75+
flagSet.StringToStringP(params.OtelExtraAttrs, "", nil, params.OtelExtraAttrsDesc)
6676
params.AddGHActionsFlags(flagSet)
6777
params.AddCirrusFlags(flagSet)
6878
c.PersistentFlags().AddFlagSet(flagSet)

cmd/mapt/cmd/params/params.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ const (
134134
VPCPublicSubnetID string = "vpc-public-subnet-id"
135135
VPCPublicSubnetIDDesc string = "ID of an existing VPC subnet (with public gateway, connected to Transit Gateway) for the SSH bastion"
136136

137+
OtelAppCode string = "otel-app-code"
138+
OtelAppCodeDesc string = "OpenTelemetry appcode identifier (e.g. MAPT-001); when set together with --otel-auth-token, installs the otelcol-contrib filelog collector on the instance"
139+
OtelAuthToken string = "otel-auth-token"
140+
OtelAuthTokenDesc string = "OpenTelemetry authentication token (UUID) used to authenticate against the OTLP endpoint"
141+
OtelEndpoint string = "otel-endpoint"
142+
OtelEndpointDesc string = "OTLP HTTP endpoint to export logs to"
143+
OtelIndex string = "otel-index"
144+
OtelIndexDesc string = "Splunk index name for log routing (e.g. rh_linux)"
145+
OtelExtraAttrs string = "otel-extra-attrs"
146+
OtelExtraAttrsDesc string = "Additional resource attributes to attach to all otelcol log records (key=value pairs)"
147+
137148
// Kind
138149
KindCmd = "kind"
139150
KindCmdDesc = "Manage a Kind cluster. This is not intended for production use"

docs/ibmcloud/ibm-power.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ On first boot, cloud-init automatically configures the PowerVS instance for on-p
2121

2222
| Variable | Required | Description |
2323
|---|---|---|
24+
| `IBMCLOUD_ACCOUNT` | yes | IBM Cloud account ID |
2425
| `IBMCLOUD_API_KEY` | yes | IBM Cloud API key |
2526
| `IC_REGION` | yes | IBM Cloud region (e.g. `us-south`, `us-east`) |
2627

@@ -41,6 +42,9 @@ Flags:
4142
-h, --help help for create
4243
--it-cirrus-pw-labels stringToString additional labels to use on the persistent worker (--it-cirrus-pw-labels key1=value1,key2=value2) (default [])
4344
--it-cirrus-pw-token string Add mapt target as a cirrus persistent worker. The value will hold a valid token to be used by cirrus cli to join the project.
45+
--otel-app-code string OpenTelemetry appcode identifier (e.g. MAPT-001); when set together with --otel-auth-token, installs the otelcol-contrib filelog collector on the instance
46+
--otel-auth-token string OpenTelemetry authentication token (UUID) used to authenticate against the OTLP endpoint
47+
--otel-endpoint string OTLP HTTP endpoint to export logs to (default "https://otel-input.corp.redhat.com")
4448
--pi-private-subnet-id string ID of an existing Power VS private subnet to attach the instance to
4549
--tags stringToString tags to add on each resource (--tags name1=value1,name2=value2) (default [])
4650
--vpc-public-subnet-id string ID of an existing VPC subnet (with public gateway, connected to Transit Gateway) for the SSH bastion
@@ -110,6 +114,26 @@ podman run -d --name ibm-power \
110114
--pi-private-subnet-id <private-subnet-id>
111115
```
112116

117+
## OpenTelemetry log collection
118+
119+
When both `--otel-app-code` and `--otel-auth-token` are provided, cloud-init installs `otelcol-contrib` on the PowerVS instance at first boot and configures it to ship `/var/log/messages` and `/var/log/secure` to the OTLP endpoint.
120+
121+
```bash
122+
podman run -d --name ibm-power \
123+
-v ${PWD}:/workspace:z \
124+
-e IBMCLOUD_API_KEY=XXX \
125+
-e IC_REGION=us-south \
126+
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-power create \
127+
--project-name ibm-power \
128+
--backed-url file:///workspace \
129+
--conn-details-output /workspace \
130+
--workspace-id <workspace-id> \
131+
--pi-private-subnet-id <private-subnet-id> \
132+
--vpc-public-subnet-id <vpc-subnet-id> \
133+
--otel-app-code MAPT-001 \
134+
--otel-auth-token <uuid-token>
135+
```
136+
113137
## Destroy
114138

115139
```bash

docs/ibmcloud/ibm-z.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Two networking modes are supported:
1111

1212
| Variable | Required | Description |
1313
|---|---|---|
14+
| `IBMCLOUD_ACCOUNT` | yes | IBM Cloud account ID |
1415
| `IBMCLOUD_API_KEY` | yes | IBM Cloud API key |
1516
| `IC_REGION` | yes | IBM Cloud region (e.g. `us-south`, `us-east`) |
1617
| `IC_ZONE` | only without `--subnet-id` | Availability zone (e.g. `us-south-2`) |
@@ -32,6 +33,9 @@ Flags:
3233
-h, --help help for create
3334
--it-cirrus-pw-labels stringToString additional labels to use on the persistent worker (--it-cirrus-pw-labels key1=value1,key2=value2) (default [])
3435
--it-cirrus-pw-token string Add mapt target as a cirrus persistent worker. The value will hold a valid token to be used by cirrus cli to join the project.
36+
--otel-app-code string OpenTelemetry appcode identifier (e.g. MAPT-001); when set together with --otel-auth-token, installs the otelcol-contrib filelog collector on the instance
37+
--otel-auth-token string OpenTelemetry authentication token (UUID) used to authenticate against the OTLP endpoint
38+
--otel-endpoint string OTLP HTTP endpoint to export logs to (default "https://otel-input.corp.redhat.com")
3539
--subnet-id string ID of an existing VPC subnet to deploy the instance into (optional)
3640
--tags stringToString tags to add on each resource (--tags name1=value1,name2=value2) (default [])
3741

@@ -90,6 +94,24 @@ podman run -d --name ibm-z \
9094
--conn-details-output /workspace
9195
```
9296

97+
## OpenTelemetry log collection
98+
99+
When both `--otel-app-code` and `--otel-auth-token` are provided, cloud-init installs `otelcol-contrib` on the instance at first boot and configures it to ship `/var/log/syslog` and `/var/log/auth.log` to the OTLP endpoint.
100+
101+
```bash
102+
podman run -d --name ibm-z \
103+
-v ${PWD}:/workspace:z \
104+
-e IBMCLOUD_API_KEY=XXX \
105+
-e IC_REGION=us-south \
106+
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-z create \
107+
--project-name ibm-z \
108+
--backed-url file:///workspace \
109+
--conn-details-output /workspace \
110+
--subnet-id <subnet-id> \
111+
--otel-app-code MAPT-001 \
112+
--otel-auth-token <uuid-token>
113+
```
114+
93115
## Destroy
94116

95117
```bash
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,149 @@
11
#cloud-config
2+
{{- if and .AppCode .OtelAuthToken .OtelIndex}}
3+
write_files:
4+
- path: /etc/otelcol-contrib/config.yaml
5+
permissions: '0640'
6+
content: |
7+
receivers:
8+
filelog/syslog:
9+
include:
10+
- /var/log/messages
11+
start_at: end
12+
include_file_path: true
13+
include_file_name: true
14+
exclude_older_than: 24h
15+
operators:
16+
- type: move
17+
id: move_to_source_name
18+
from: attributes["log.file.path"]
19+
to: attributes["_sourceName"]
20+
- type: remove
21+
id: remove_file_name
22+
field: attributes["log.file.name"]
23+
- type: time_parser
24+
id: parse_timestamp
25+
layout: '%b %e %H:%M:%S'
26+
parse_from: body
27+
on_error: send
28+
attributes:
29+
index: "{{.OtelIndex}}"
30+
_sourceCategory: syslog
31+
_sourceHost: ${env:HOSTNAME}
32+
filelog/secure:
33+
include:
34+
- /var/log/secure
35+
start_at: end
36+
include_file_path: true
37+
include_file_name: true
38+
exclude_older_than: 24h
39+
operators:
40+
- type: move
41+
id: move_to_source_name
42+
from: attributes["log.file.path"]
43+
to: attributes["_sourceName"]
44+
- type: remove
45+
id: remove_file_name
46+
field: attributes["log.file.name"]
47+
- type: time_parser
48+
id: parse_timestamp
49+
layout: '%b %e %H:%M:%S'
50+
parse_from: body
51+
on_error: send
52+
attributes:
53+
index: "{{.OtelIndex}}"
54+
_sourceCategory: secure
55+
_sourceHost: ${env:HOSTNAME}
56+
filelog/audit:
57+
include:
58+
- /var/log/audit/audit.log
59+
start_at: end
60+
include_file_path: true
61+
include_file_name: true
62+
exclude_older_than: 24h
63+
operators:
64+
- type: move
65+
id: move_to_source_name
66+
from: attributes["log.file.path"]
67+
to: attributes["_sourceName"]
68+
- type: remove
69+
id: remove_file_name
70+
field: attributes["log.file.name"]
71+
attributes:
72+
index: "{{.OtelIndex}}"
73+
_sourceCategory: audit
74+
_sourceHost: ${env:HOSTNAME}
75+
processors:
76+
filter/drop_null_bytes:
77+
logs:
78+
log_record:
79+
- 'IsMatch(body, "^\x00+$")'
80+
batch:
81+
timeout: "1s"
82+
send_batch_size: 1024
83+
resource:
84+
attributes:
85+
- key: appcode
86+
value: "{{.AppCode}}"
87+
action: upsert
88+
- key: com.redhat.otel.auth_token
89+
value: "${env:OTEL_AUTH_TOKEN}"
90+
action: upsert
91+
- key: arch
92+
value: "{{.OtelArch}}"
93+
action: upsert
94+
{{- range $k, $v := .OtelExtraAttrs}}
95+
- key: {{$k}}
96+
value: "{{$v}}"
97+
action: upsert
98+
{{- end}}
99+
exporters:
100+
otlphttp:
101+
endpoint: "{{.OtelEndpoint}}"
102+
tls:
103+
insecure_skip_verify: true
104+
service:
105+
telemetry:
106+
logs:
107+
level: "fatal"
108+
metrics:
109+
level: "basic"
110+
pipelines:
111+
logs:
112+
receivers: [filelog/syslog, filelog/secure, filelog/audit]
113+
processors: [filter/drop_null_bytes, resource, batch]
114+
exporters: [otlphttp]
115+
- path: /etc/otelcol-contrib/auth_token
116+
permissions: '0600'
117+
content: |
118+
OTEL_AUTH_TOKEN={{.OtelAuthToken}}
119+
- path: /etc/systemd/system/otelcol-contrib.service.d/capabilities.conf
120+
permissions: '0644'
121+
content: |
122+
[Service]
123+
AmbientCapabilities=CAP_DAC_READ_SEARCH
124+
Environment="HOSTNAME=%H"
125+
EnvironmentFile=/etc/otelcol-contrib/auth_token
126+
{{- end}}
2127
runcmd:
3128
- |
4129
IFACE=$(ip route show default | awk '/default/ {print $5; exit}')
5130
ip route add 10.0.0.0/8 via {{.Gateway}} dev "$IFACE" 2>/dev/null || true
6131
echo "10.0.0.0/8 via {{.Gateway}}" > "/etc/sysconfig/network-scripts/route-$IFACE"
132+
{{- if and .AppCode .OtelAuthToken .OtelIndex}}
133+
- |
134+
PROXY_URL=""
135+
if ! curl -sf --connect-timeout 5 --head {{.OtelEndpoint}} > /dev/null 2>&1; then
136+
PROXY_URL="http://squid.corp.redhat.com:3128"
137+
fi
138+
RPM_URL="https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{.OtelColVersion}}/otelcol-contrib_{{.OtelColVersion}}_linux_ppc64le.rpm"
139+
HTTPS_PROXY="$PROXY_URL" curl -fsSL -o /tmp/otelcol-contrib.rpm "$RPM_URL"
140+
rpm -i /tmp/otelcol-contrib.rpm
141+
rm -f /tmp/otelcol-contrib.rpm
142+
chown -R otelcol-contrib:otelcol-contrib /etc/otelcol-contrib
143+
if [ -n "$PROXY_URL" ]; then
144+
printf '[Service]\nEnvironment="HTTPS_PROXY=%s/"\nEnvironment="NO_PROXY=10.*,192.168.*,localhost,127.0.0.1"\n' "$PROXY_URL" \
145+
> /etc/systemd/system/otelcol-contrib.service.d/proxy.conf
146+
fi
147+
systemctl daemon-reload
148+
systemctl enable --now otelcol-contrib
149+
{{- end}}

0 commit comments

Comments
 (0)