diff --git a/.goreleaser.yml b/.goreleaser.yml index 4054205bf..1420cb2e8 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -21,9 +21,10 @@ builds: tags: - bindplane goos: - - windows - - linux + - aix - darwin + - linux + - windows goarch: - amd64 - arm64 @@ -31,6 +32,18 @@ builds: - ppc64 - ppc64le ignore: + - goos: aix + goarch: arm64 + - goos: aix + goarch: arm + - goos: aix + goarch: amd64 + - goos: aix + goarch: ppc64le + - goos: darwin + goarch: ppc64 + - goos: darwin + goarch: ppc64le - goos: windows goarch: arm - goos: windows @@ -39,20 +52,23 @@ builds: goarch: ppc64 - goos: windows goarch: ppc64le - - goos: darwin - goarch: ppc64 - - goos: darwin - goarch: ppc64le prebuilt: path: tmp/collector_{{ .Os }}_{{ .Arch }}{{ .Ext }} + ldflags: + - -s -w + - -X github.com/observiq/bindplane-agent/internal/version.version=v{{ .Version }} + - -X github.com/observiq/bindplane-agent/internal/version.gitHash={{ .FullCommit }} + - -X github.com/observiq/bindplane-agent/internal/version.date={{ .Date }} + no_unique_dist_dir: false - id: updater builder: prebuilt binary: updater mod_timestamp: "{{ .CommitTimestamp }}" goos: - - windows - - linux + - aix - darwin + - linux + - windows goarch: - amd64 - arm64 @@ -60,6 +76,18 @@ builds: - ppc64 - ppc64le ignore: + - goos: aix + goarch: amd64 + - goos: aix + goarch: arm64 + - goos: aix + goarch: arm + - goos: aix + goarch: ppc64le + - goos: darwin + goarch: ppc64 + - goos: darwin + goarch: ppc64le - goos: windows goarch: arm - goos: windows @@ -68,19 +96,16 @@ builds: goarch: ppc64 - goos: windows goarch: ppc64le - - goos: darwin - goarch: ppc64 - - goos: darwin - goarch: ppc64le prebuilt: path: tmp/updater_{{ .Os }}_{{ .Arch }}{{ .Ext }} - id: supervisor builder: prebuilt binary: opampsupervisor goos: - - windows - - linux + - aix - darwin + - linux + - windows goarch: - amd64 - arm64 @@ -88,6 +113,18 @@ builds: - ppc64 - ppc64le ignore: + - goos: aix + goarch: amd64 + - goos: aix + goarch: arm64 + - goos: aix + goarch: arm + - goos: aix + goarch: ppc64le + - goos: darwin + goarch: ppc64 + - goos: darwin + goarch: ppc64le - goos: windows goarch: arm - goos: windows @@ -96,12 +133,14 @@ builds: goarch: ppc64 - goos: windows goarch: ppc64le - - goos: darwin - goarch: ppc64 - - goos: darwin - goarch: ppc64le prebuilt: path: release_deps/supervisor_bin/opampsupervisor_{{ .Os }}_{{ .Arch }}{{ .Ext }} + ldflags: + - -s -w + - -X github.com/observiq/bindplane-agent/updater/internal/version.version=v{{ .Version }} + - -X github.com/observiq/bindplane-agent/updater/internal/version.gitHash={{ .FullCommit }} + - -X github.com/observiq/bindplane-agent/updater/internal/version.date={{ .Date }} + no_unique_dist_dir: false # https://goreleaser.com/customization/archive/ archives: @@ -133,6 +172,9 @@ archives: - src: release_deps/sysconfig/observiq-otel-collector dst: "install/sysconfig" strip_parent: true + - src: release_deps/observiq-otel-collector.aix.env + dst: "install" + strip_parent: true format_overrides: - goos: windows format: zip @@ -217,6 +259,8 @@ nfpms: mode: 0644 owner: root group: root + # This file will be unused on modern systemd Linux + # The postinstall.sh file removes this if it isn't needed - src: service/observiq-otel-collector dst: /etc/init.d/observiq-otel-collector type: config|noreplace @@ -224,6 +268,8 @@ nfpms: mode: 0755 owner: root group: root + # This file will be unused on modern systemd Linux + # The postinstall.sh file removes this if it isn't needed - src: service/sysconfig/observiq-otel-collector dst: /etc/sysconfig/observiq-otel-collector type: config @@ -695,6 +741,7 @@ release: # https://goreleaser.com/customization/changelog/ changelog: + disable: false use: github sort: asc groups: diff --git a/Makefile b/Makefile index 4247ecef5..74d7d0fc5 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,12 @@ agent: builder --config="./manifests/observIQ/manifest.yaml" --ldflags "-s -w -X github.com/observiq/bindplane-agent/internal/version.version=$(VERSION)" mkdir -p $(OUTDIR); cp ./builder/observiq-otel-collector $(OUTDIR)/collector_$(GOOS)_$(GOARCH)$(EXT) +# Builds the agent for current GOOS/GOARCH pair (aix) +.PHONY: agent-aix +agent-aix: + builder --config="./manifests/observIQ/manifest-aix.yaml" --ldflags "-s -w -X github.com/observiq/bindplane-agent/internal/version.version=$(VERSION)" + mkdir -p $(OUTDIR); cp ./builder/observiq-otel-collector $(OUTDIR)/collector_$(GOOS)_$(GOARCH)$(EXT) + # Builds a custom distro for the current GOOS/GOARCH pair using the manifest specified # MANIFEST = path to the manifest file for the distro to be built # Usage: make distro MANIFEST="./manifests/custom/my_distro_manifest.yaml" @@ -71,12 +77,22 @@ reset: kill .PHONY: build-binaries build-binaries: agent updater +# Builds the updater + agent for current GOOS/GOARCH pair +.PHONY: build-binaries-aix +build-binaries-aix: agent-aix updater + .PHONY: build-all -build-all: build-linux build-darwin build-windows +build-all: build-linux build-unix build-windows .PHONY: build-linux build-linux: build-linux-amd64 build-linux-arm64 build-linux-arm build-linux-ppc64 build-linux-ppc64le +.PHONY: build-unix +build-unix: build-darwin build-aix + +.PHONY: build-aix +build-aix: build-aix-ppc64 + .PHONY: build-darwin build-darwin: build-darwin-amd64 build-darwin-arm64 @@ -103,6 +119,10 @@ build-linux-arm64: build-linux-arm: GOOS=linux GOARCH=arm $(MAKE) build-binaries -j2 +.PHONY: build-aix-ppc64 +build-aix-ppc64: + GOOS=aix GOARCH=ppc64 $(MAKE) build-binaries-aix -j2 + .PHONY: build-darwin-amd64 build-darwin-amd64: GOOS=darwin GOARCH=amd64 $(MAKE) build-binaries -j2 @@ -251,6 +271,7 @@ release-prep: @jq ".files[] | select(.service != null)" windows/wix.json >> release_deps/windows_service.json @cp service/observiq-otel-collector.service release_deps/observiq-otel-collector.service @cp service/observiq-otel-collector release_deps/observiq-otel-collector + @cp service/observiq-otel-collector.aix.env release_deps/observiq-otel-collector.aix.env @cp -r ./service/sysconfig release_deps/ # Build and sign, skip release and ignore dirty git tree diff --git a/manifests/observIQ/manifest-aix.yaml b/manifests/observIQ/manifest-aix.yaml new file mode 100644 index 000000000..757f4b4fa --- /dev/null +++ b/manifests/observIQ/manifest-aix.yaml @@ -0,0 +1,108 @@ +# This manifest is a combination of the upstream opentelemetry-collector-contrib manifest & components unique to observIQ's distribution. +# https://github.com/open-telemetry/opentelemetry-collector-releases/blob/main/distributions/otelcol-contrib/manifest.yaml +dist: + module: github.com/observiq/bindplane-agent + name: observiq-otel-collector + description: ObservIQ's custom distro for OpenTelemetry Collector + version: "v2.0.0" + output_path: ./builder + otelcol_version: 0.111.0 +conf_resolver: + default_uri_scheme: "env" +extensions: + - gomod: go.opentelemetry.io/collector/extension/zpagesextension v0.111.0 + # ballastextension deprecated, keeping at v0.108.1 + - gomod: go.opentelemetry.io/collector/extension/ballastextension v0.108.1 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/ackextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/asapauthextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/awsproxy v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/bearertokenauthextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/encoding/jaegerencodingextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/encoding/otlpencodingextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/encoding/zipkinencodingextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/httpforwarderextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/dockerobserver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/ecsobserver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/ecstaskobserver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/hostobserver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/k8sobserver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/oidcauthextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/opampextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/dbstorage v0.111.0 + - gomod: github.com/observiq/bindplane-agent/extension/bindplaneextension v1.61.1 + path: "./extension/bindplaneextension" +exporters: + - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.111.0 + - gomod: go.opentelemetry.io/collector/exporter/nopexporter v0.111.0 + - gomod: go.opentelemetry.io/collector/exporter/otlpexporter v0.111.0 + - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.111.0 +processors: + - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor v0.111.0 + - gomod: github.com/observiq/bindplane-agent/processor/snapshotprocessor v1.61.1 + path: "./processor/snapshotprocessor" + - gomod: github.com/observiq/bindplane-agent/processor/throughputmeasurementprocessor v1.61.1 + path: "./processor/throughputmeasurementprocessor" + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/groupbyattrsprocessor v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/groupbytraceprocessor v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor v0.111.0 +receivers: + - gomod: go.opentelemetry.io/collector/receiver/nopreceiver v0.111.0 + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/collectdreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/filelogreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jmxreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/saphanareceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/sapmreceiver v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/statsdreceiver v0.111.0 + - gomod: github.com/observiq/bindplane-agent/receiver/pluginreceiver v1.61.1 + path: "./receiver/pluginreceiver" + - gomod: github.com/observiq/bindplane-agent/receiver/routereceiver v1.61.1 + path: "./receiver/routereceiver" + - gomod: github.com/observiq/bindplane-agent/receiver/telemetrygeneratorreceiver v1.61.1 + path: "./receiver/telemetrygeneratorreceiver" +connectors: + - gomod: go.opentelemetry.io/collector/connector/forwardconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/exceptionsconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/grafanacloudconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/roundrobinconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector v0.111.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector v0.111.0 +providers: + - gomod: go.opentelemetry.io/collector/confmap/provider/envprovider v1.17.0 + - gomod: go.opentelemetry.io/collector/confmap/provider/fileprovider v1.17.0 + - gomod: go.opentelemetry.io/collector/confmap/provider/httpprovider v1.17.0 + - gomod: go.opentelemetry.io/collector/confmap/provider/httpsprovider v1.17.0 + - gomod: go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.17.0 +# When adding a replace, add a comment before it to document why it's needed and when it can be removed +replaces: + # See https://github.com/google/gnostic/issues/262 + - github.com/googleapis/gnostic v0.5.6 => github.com/googleapis/gnostic v0.5.5 + # See https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/12322#issuecomment-1185029670 + - github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 => github.com/docker/go-connections v0.4.0 + # see https://github.com/mattn/go-ieproxy/issues/45 + - github.com/mattn/go-ieproxy => github.com/mattn/go-ieproxy v0.0.1 + # see https://github.com/openshift/api/pull/1515 + - github.com/openshift/api => github.com/openshift/api v0.0.0-20230726162818-81f778f3b3ec + # replace for datapointcountprocessor + - github.com/observiq/bindplane-agent/receiver/routereceiver => ../receiver/routereceiver + # replace statements for internal imports + - github.com/observiq/bindplane-agent/internal/expr => ../internal/expr + - github.com/observiq/bindplane-agent/internal/counter => ../internal/counter + - github.com/observiq/bindplane-agent/internal/report => ../internal/report + - github.com/observiq/bindplane-agent/internal/testutils => ../internal/testutils + - github.com/observiq/bindplane-agent/internal/rehydration => ../internal/rehydration diff --git a/scripts/install/install_unix.sh b/scripts/install/install_unix.sh index 06c76b68b..edcf2d9a4 100755 --- a/scripts/install/install_unix.sh +++ b/scripts/install/install_unix.sh @@ -24,13 +24,18 @@ if command -v systemctl >/dev/null 2>&1; then SVC_PRE=systemctl elif command -v service >/dev/null 2>&1; then SVC_PRE=service +elif command -v mkssys > /dev/null 2>&1; then + SVC_PRE=mkssys fi # Script Constants +COLLECTOR_USER="observiq-otel-collector" TMP_DIR=${TMPDIR:-"/tmp"} # Allow this to be overriden by cannonical TMPDIR env var INSTALL_DIR="/opt/observiq-otel-collector" SUPERVISOR_YML_PATH="$INSTALL_DIR/supervisor.yaml" PREREQS="curl printf $SVC_PRE sed uname cut" +MANAGEMENT_YML_PATH="/opt/observiq-otel-collector/manager.yaml" +PREREQS="curl printf $SVC_PRE sed uname cut tr tar sudo" SCRIPT_NAME="$0" INDENT_WIDTH=' ' indent="" @@ -38,12 +43,19 @@ indent="" # Default Supervisor Config Hash DEFAULT_SUPERVISOR_CFG_HASH="ac4e6001f1b19d371bba6a2797ba0a55d7ca73151ba6908040598ca275c0efca" -# out_file_path is the full path to the downloaded package (e.g. "/tmp/observiq-otel-collector_linux_amd64.deb") +# Require bash for AIX to create a standardized environment +if [ "$(uname -s)" != "AIX" ]; then + PREREQS="bash $PREREQS" +fi + +# out_file_path is the full path to the downloaded package (e.g. "/tmp/observiq-otel-collector_{os}_amd64.deb") out_file_path="unknown" +os="unknown" +os_arch="unknown" # Colors num_colors=$(tput colors 2>/dev/null) -if test -n "$num_colors" && test "$num_colors" -ge 8; then +if [ "$(uname -s)" != AIX ] && test -n "$num_colors" && test "$num_colors" -ge 8; then bold="$(tput bold)" underline="$(tput smul)" # standout can be bold or reversed colors dependent on terminal @@ -75,8 +87,8 @@ fi # Helper Functions printf() { - if command -v sed >/dev/null; then - command printf -- "$@" | sed -r "$sed_ignore s/^/$indent/g" # Ignore sole reset characters if defined + if [ "$(uname -s)" != "AIX" ] && command -v sed >/dev/null; then + command printf -- "$@" | sed -n "$sed_ignore s/^/$indent/g" # Ignore sole reset characters if defined else # Ignore $* suggestion as this breaks the output # shellcheck disable=SC2145 @@ -182,7 +194,7 @@ Usage: Example: '-l http://my.domain.org/observiq-otel-collector' will download from there. $(fg_yellow '-b, --base-url') - Defines the base of the download URL as '{base_url}/v{version}/{PACKAGE_NAME}_v{version}_linux_{os_arch}.{package_type}'. + Defines the base of the download URL as '{base_url}/v{version}/{PACKAGE_NAME}_v{version}_{os}_{os_arch}.{package_type}'. If not provided, this will default to '$DOWNLOAD_BASE'. Example: '-b http://my.domain.org/observiq-otel-collector/binaries' will be used as the base of the download URL. @@ -302,7 +314,11 @@ setup_installation() { set_proxy set_file_name else - out_file_path="$package_path" + if [ ! -f "$package_path" ]; then + error_exit "$LINENO" "--file specified, but '$package_path' does not exist" + else + out_file_path="$package_path" + fi fi set_opamp_endpoint @@ -316,10 +332,10 @@ setup_installation() { } set_file_name() { - if [ -z "$version" ]; then - package_file_name="${PACKAGE_NAME}_linux_${arch}.${package_type}" + if [ -z "$version" ] ; then + package_file_name="${PACKAGE_NAME}_${os}_${os_arch}.${package_type}" else - package_file_name="${PACKAGE_NAME}_v${version}_linux_${arch}.${package_type}" + package_file_name="${PACKAGE_NAME}_v${version}_${os}_${os_arch}.${package_type}" fi out_file_path="$TMP_DIR/$package_file_name" } @@ -351,37 +367,47 @@ set_proxy() { fi } -set_os_arch() { - os_arch=$(uname -m) - case "$os_arch" in - # arm64 strings. These are from https://stackoverflow.com/questions/45125516/possible-values-for-uname-m - aarch64 | arm64 | aarch64_be | armv8b | armv8l) - os_arch="arm64" - ;; - x86_64) - os_arch="amd64" - ;; - # experimental PowerPC arch support for collector - ppc64) - os_arch="ppc64" - ;; - ppc64le) - os_arch="ppc64le" - ;; - # armv6/32bit. These are what raspberry pi can return, which is the main reason we support 32-bit arm - arm | armv6l | armv7l) - os_arch="arm" - ;; - *) - error_exit "$LINENO" "Unsupported os arch: $os_arch" - ;; + +set_os_arch() +{ + case "$os_arch" in + # arm64 strings. These are from https://stackoverflow.com/questions/45125516/possible-values-for-uname-m + aarch64|arm64|aarch64_be|armv8b|armv8l) + os_arch="arm64" + ;; + x86_64) + os_arch="amd64" + ;; + # experimental PowerPC arch support for collector + ppc64) + os_arch="ppc64" + ;; + ppc64le) + os_arch="ppc64le" + ;; + powerpc) + if [ "$(uname -s)" = "AIX" ] && command -v bootinfo > /dev/null && [ "$(bootinfo -y)" = "64" ]; then + os_arch="ppc64" + elif command -v bootinfo > /dev/null; then + error_exit "$LINENO" "Command 'bootinfo' not found, OS likely isn't AIX, but we expect it to be" + else + error_exit "$LINENO" "uname returned arch of 'powerpc', but OS is either not AIX or not 64 bit. 'uname -s': $(uname -s), 'bootinfo -y': $(bootinfo -y)" + fi + ;; + # armv6/32bit. These are what raspberry pi can return, which is the main reason we support 32-bit arm + arm|armv6l|armv7l) + os_arch="arm" + ;; + *) + error_exit "$LINENO" "Unsupported os arch: $os_arch" + ;; esac } # Set the package type before install set_package_type() { # if package_path is set get the file extension otherwise look at what's available on the system - if [ -n "$package_path" ]; then + if [ -n "$package_path" ] && [ "$(uname -s)" != "AIX" ]; then case "$package_path" in *.deb) package_type="deb" @@ -394,12 +420,14 @@ set_package_type() { ;; esac else - if command -v dpkg >/dev/null 2>&1; then - package_type="deb" - elif command -v rpm >/dev/null 2>&1; then - package_type="rpm" + if command -v dpkg > /dev/null 2>&1; then + package_type="deb" + elif command -v mkssys > /dev/null 2>&1; then + package_type="mkssys" + elif command -v rpm > /dev/null 2>&1; then + package_type="rpm" else - error_exit "$LINENO" "Could not find dpkg or rpm on the system" + error_exit "$LINENO" "Could not find mkssys, dpkg or rpm on the system" fi fi @@ -427,7 +455,7 @@ set_download_urls() { base_url=$DOWNLOAD_BASE fi - collector_download_url="$base_url/v$version/${PACKAGE_NAME}_v${version}_linux_${os_arch}.${package_type}" + collector_download_url="$base_url/v$version/${PACKAGE_NAME}_v${version}_${os}_${os_arch}.${package_type}" else collector_download_url="$url" fi @@ -557,28 +585,38 @@ os_check() { info "Checking that the operating system is supported..." os_type=$(uname -s) case "$os_type" in - Linux) - succeeded - ;; - *) - failed - error_exit "$LINENO" "The operating system $(fg_yellow "$os_type") is not supported by this script." - ;; + Linux) + succeeded + ;; + AIX) + succeeded + ;; + *) + failed + error_exit "$LINENO" "The operating system $(fg_yellow "$os_type") is not supported by this script." + ;; esac + + # Create lowercase os variable + os=$(echo "$os_type" | tr '[:upper:]' '[:lower:]') } # This will check if the system architecture is supported. os_arch_check() { info "Checking for valid operating system architecture..." - arch=$(uname -m) - case "$arch" in - x86_64 | aarch64 | ppc64 | ppc64le | arm64 | aarch64_be | armv8b | armv8l | arm | armv6l | armv7l) - succeeded - ;; - *) - failed - error_exit "$LINENO" "The operating system architecture $(fg_yellow "$arch") is not supported by this script." - ;; + if [ "$(uname -s)" = "AIX" ]; then + os_arch=$(uname -p) + else + os_arch=$(uname -m) + fi + case "$os_arch" in + x86_64|aarch64|powerpc|ppc64|ppc64le|arm64|aarch64_be|armv8b|armv8l|arm|armv6l|armv7l) + succeeded + ;; + *) + failed + error_exit "$LINENO" "The operating system architecture $(fg_yellow "$os_arch") is not supported by this script." + ;; esac } @@ -607,23 +645,40 @@ dependencies_check() { } # This will check to ensure either dpkg or rpm is installed on the system -package_type_check() { +package_type_check() +{ info "Checking for package manager..." - if command -v dpkg >/dev/null 2>&1; then - succeeded - elif command -v rpm >/dev/null 2>&1; then - succeeded + if command -v dpkg > /dev/null 2>&1; then + succeeded + # Check ALL of the AIX commands needed + elif command -v mkssys > /dev/null 2>&1 \ + && command -v mkgroup > /dev/null 2>&1 \ + && command -v useradd > /dev/null 2>&1 \ + && command -v startsrc > /dev/null 2>&1 \ + && command -v stopsrc > /dev/null 2>&1 \ + && command -v lssrc > /dev/null 2>&1 \ + && command -v mkitab > /dev/null 2>&1 \ + && command -v rmitab > /dev/null 2>&1 \ + && command -v lsitab > /dev/null 2>&1; then + succeeded + elif command -v rpm > /dev/null 2>&1; then + succeeded else - failed - error_exit "$LINENO" "Could not find dpkg or rpm on the system" + failed + if [ "$(uname -s)" = "AIX" ]; then + error_exit "$LINENO" "Could not find AIX installation tools on the system" + else + error_exit "$LINENO" "Could not find dpkg or rpm on the system" + fi fi } # latest_version gets the tag of the latest release, without the v prefix. -latest_version() { - curl -sSL -H"Accept: application/vnd.github.v3+json" https://api.github.com/repos/observIQ/observiq-otel-collector/releases/latest | - grep "\"tag_name\"" | - sed -r 's/ *"tag_name": "v([0-9]+\.[0-9]+\.[0-9+])",/\1/' +latest_version() +{ + curl -sSL -H"Accept: application/vnd.github.v3+json" https://api.github.com/repos/observIQ/observiq-otel-collector/releases/latest | \ + grep "\"tag_name\"" | \ + cut -d: -f2 | tr -d '"' | tr -d ' ' | tr -d ',' | tr -d 'v' } # This will install the package by downloading the archived agent, @@ -654,11 +709,12 @@ install_package() { info "Installing package..." # if target install directory doesn't exist and we're using dpkg ensure a clean state # by checking for the package and running purge if it exists. - if [ ! -d "$INSTALL_DIR" ] && [ "$package_type" = "deb" ]; then - dpkg -s "observiq-otel-collector" >/dev/null 2>&1 && dpkg --purge "observiq-otel-collector" >/dev/null 2>&1 + if [ ! -d "/opt/observiq-otel-collector" ] && [ "$package_type" = "deb" ]; then + info "Running dpkg --purge to ensure a clean state" + dpkg -s "observiq-otel-collector" > /dev/null 2>&1 && dpkg --purge "observiq-otel-collector" > /dev/null 2>&1 fi - unpack_package || error_exit "$LINENO" "Failed to extract package" + install_package_file || error_exit "$LINENO" "Failed to extract package" succeeded create_supervisor_config "$SUPERVISOR_YML_PATH" @@ -676,7 +732,7 @@ install_package() { systemctl enable --now observiq-otel-collector >/dev/null 2>&1 || error_exit "$LINENO" "Failed to enable service" succeeded fi - else + elif [ "$SVC_PRE" = "service" ]; then case "$(service observiq-otel-collector status)" in *running*) # The service is running. @@ -692,24 +748,97 @@ install_package() { succeeded ;; esac + elif [ "$SVC_PRE" = "mkssys" ]; then + case "$(lssrc -s observiq-otel-collector | grep collector)" in + *active*) + # The service is running. + # We'll want to restart. + info "Restarting service..." + # AIX does not support service "restart", so stop and start instead + stopsrc -s observiq-otel-collector + # Start the service with the proper environment variables + startsrc -s observiq-otel-collector -a start -e "$(cat /etc/observiq-otel-collector.aix.env)" + succeeded + ;; + *inoperative*) + info "Starting service..." + # Start the service with the proper environment variables + startsrc -s observiq-otel-collector -a start -e "$(cat /etc/observiq-otel-collector.aix.env)" + succeeded + ;; + *) + info "Creating, enabling and starting service..." + # Add the service, removing it if it already exists in order + # to make sure we have the most recent version + if lssrc -s observiq-otel-collector > /dev/null 2>&1; then + rmssys -s observiq-otel-collector + else + mkssys -s observiq-otel-collector -p /opt/observiq-otel-collector/observiq-otel-collector -u "$(id -u observiq-otel-collector)" -S -n15 -f9 -a '--config config.yaml --manager manager.yaml --logging logging.yaml' + fi + + # Install the service to start on boot + # Removing it if it exists, in order to have the most recent version + if lsitab oiqcollector > /dev/null 2>&1; then + rmitab oiqcollector + else + # shellcheck disable=SC2016 + mkitab 'oiqcollector:23456789:respawn:startsrc -s observiq-otel-collector -a start -e "$(cat /etc/observiq-otel-collector.aix.env)"' + fi + + # Start the service with the proper environment variables + startsrc -s observiq-otel-collector -a start -e "$(cat /etc/observiq-otel-collector.aix.env)" + + succeeded + ;; + esac + else + # This is an error state that should never be reached + error_exit "$LINENO" "Found an invalid SVC_PRE value in install_package()" fi success "BindPlane Agent installation complete!" decrease_indent } -unpack_package() { +# Install on AIX manually from tar.gz file +install_aix() +{ + # Create the observiq-otel-collector user and group. Group must be first. + mkgroup "$COLLECTOR_USER" > /dev/null 2>&1 + useradd -d /opt/observiq-otel-collector -g "$COLLECTOR_USER" -s "$(which bash)" "$COLLECTOR_USER" > /dev/null 2>&1 + + # Create the install & storage directories + mkdir -p /opt/observiq-otel-collector/storage > /dev/null 2>&1 + + # Extract + zcat "$out_file_path" | tar -Uxvf - -C /opt/observiq-otel-collector > /dev/null 2>&1 + + # Move files to appropriate locations + mv /opt/observiq-otel-collector/opentelemetry-java-contrib-jmx-metrics.jar /opt/ > /dev/null 2>&1 + mv /opt/observiq-otel-collector/install/observiq-otel-collector.aix.env /etc/ > /dev/null 2>&1 + + # Set ownership + chown -R "$COLLECTOR_USER":"$COLLECTOR_USER" /opt/observiq-otel-collector > /dev/null 2>&1 + chown "$COLLECTOR_USER":"$COLLECTOR_USER" /opt/opentelemetry-java-contrib-jmx-metrics.jar > /dev/null 2>&1 + chown "$COLLECTOR_USER":"$COLLECTOR_USER" /etc/observiq-otel-collector.aix.env > /dev/null 2>&1 +} + +install_package_file() +{ case "$package_type" in - deb) - dpkg --force-confold -i "$out_file_path" >/dev/null || error_exit "$LINENO" "Failed to unpack package" - ;; - rpm) - rpm -U "$out_file_path" >/dev/null || error_exit "$LINENO" "Failed to unpack package" - ;; - *) - error "Unrecognized package type" - return 1 - ;; + deb) + dpkg --force-confold -i "$out_file_path" > /dev/null || error_exit "$LINENO" "Failed to unpack package" + ;; + mkssys) + install_aix + ;; + rpm) + rpm -U "$out_file_path" > /dev/null || error_exit "$LINENO" "Failed to unpack package" + ;; + *) + error "Unrecognized package type" + return 1 + ;; esac return 0 } @@ -773,9 +902,15 @@ display_results() { if [ "$SVC_PRE" = "systemctl" ]; then info "Supervisor Start Command: $(fg_cyan "sudo systemctl start observiq-otel-collector")$(reset)" info "Supervisor Stop Command: $(fg_cyan "sudo systemctl stop observiq-otel-collector")$(reset)" - else + info "Supervisor Status Command: $(fg_cyan "sudo systemctl status observiq-otel-collector")$(reset)" + elif [ "$SVC_PRE" = "service" ]; then info "Supervisor Start Command: $(fg_cyan "sudo service observiq-otel-collector start")$(reset)" info "Supervisor Stop Command: $(fg_cyan "sudo service observiq-otel-collector stop")$(reset)" + info "Supervisor Status Command: $(fg_cyan "sudo service observiq-otel-collector status")$(reset)" + elif [ "$SVC_PRE" = "mkssys" ]; then + info "Supervisor Start Command: $(fg_cyan "sudo startsrc -s observiq-otel-collector -a start -e "$(cat /opt/observiq-otel-collector/observiq-otel-collector.env)"")$(reset)" + info "Supervisor Stop Command: $(fg_cyan "sudo stopsrc -s observiq-otel-collector")$(reset)" + info "Supervisor Status Command: $(fg_cyan "sudo lssrc -s observiq-otel-collector")$(reset)" fi decrease_indent @@ -790,11 +925,121 @@ display_results() { decrease_indent decrease_indent - banner "$(fg_green Installation Complete!)" + banner "$(fg_green Installation Complete!)" + return 0 +} + +uninstall_aix() +{ + # Remove files + rm -rf /opt/observiq-otel-collector > /dev/null 2>&1 + rm -f /opt/opentelemetry-java-contrib-jmx-metrics.jar > /dev/null 2>&1 + rm -f /etc/observiq-otel-collector.aix.env > /dev/null 2>&1 +} + +uninstall_package() +{ + case "$package_type" in + deb) + dpkg -r "observiq-otel-collector" > /dev/null 2>&1 + ;; + mkssys) + uninstall_aix + ;; + rpm) + rpm -e "observiq-otel-collector" > /dev/null 2>&1 + ;; + *) + error "Unrecognized package type" + return 1 + ;; + esac return 0 } -main() { +uninstall() +{ + bindplane_banner + + set_package_type + banner "Uninstalling BindPlane Agent" + increase_indent + + info "Checking permissions..." + root_check + succeeded + + if [ "$SVC_PRE" = "systemctl" ]; then + info "Stopping service..." + systemctl stop observiq-otel-collector > /dev/null || error_exit "$LINENO" "Failed to stop service" + succeeded + + info "Disabling service..." + systemctl disable observiq-otel-collector > /dev/null 2>&1 || error_exit "$LINENO" "Failed to disable service" + succeeded + elif [ "$SVC_PRE" = "service" ]; then + info "Stopping service..." + service observiq-otel-collector stop + succeeded + + info "Disabling service..." + chkconfig observiq-otel-collector on + # rm -f /etc/init.d/observiq-otel-collector + succeeded + elif [ "$SVC_PRE" = "mkssys" ]; then + # Using case here to bypass =~ missing in the POSIX standard + case "$(lssrc -s observiq-otel-collector | grep collector)" in + *active*) + # The service is running. Stop it before removing it. + info "Stopping service..." + stopsrc -s observiq-otel-collector + ;; + esac + + # Remove the service + info "Disabling service..." + if lsitab oiqcollector > /dev/null 2>&1; then + # Removing start on boot for the service + rmitab oiqcollector + fi + if lssrc -s observiq-otel-collector > /dev/null 2>&1; then + # Removing actual service entry + rmssys -s observiq-otel-collector + fi + + succeeded + else + # This is an error state that should never be reached + error_exit "Found an invalid SVC_PRE value in uninstall()" + fi + + info "Removing any existing manager.yaml..." + rm -f "$MANAGEMENT_YML_PATH" + succeeded + + info "Removing package..." + uninstall_package || error_exit "$LINENO" "Failed to remove package" + succeeded + decrease_indent + + banner "$(fg_green Uninstallation Complete!)" +} + +check_aix_name_length() +{ + aix_name_size="$(lsattr -El sys0 -a max_logname | cut -d" " -f 2)" + if [ "$aix_name_size" -lt 24 ]; then + error "$LINENO" "Current system will result in '3004-694 Error adding Name is too long.' when attempting to create group and user" + error "Current max: $aix_name_size" + error "Please raise your limit to 24 characters or greater by issuing these command:" + error " chdev -lsys0 -a max_logname=" + error "and then rebooting the system before attempting to run this script again" + error_exit "Reference: https://www.ibm.com/support/pages/aix-security-change-maximum-length-user-name-group-name-or-password" + fi +} + +main() +{ # We do these checks before we process arguments, because # some of these options bail early, and we'd like to be sure that those commands # (e.g. uninstall) can run @@ -874,6 +1119,11 @@ main() { done fi + # AIX needs a special check + if [ "$os" = "aix" ]; then + check_aix_name_length + fi + connection_check setup_installation install_package diff --git a/service/observiq-otel-collector.aix.env b/service/observiq-otel-collector.aix.env new file mode 100644 index 000000000..56b7fcf5c --- /dev/null +++ b/service/observiq-otel-collector.aix.env @@ -0,0 +1 @@ +OIQ_OTEL_COLLECTOR_HOME=/opt/observiq-otel-collector OIQ_OTEL_COLLECTOR_STORAGE=/opt/observiq-otel-collector/storage \ No newline at end of file diff --git a/updater/internal/path/path_unix.go b/updater/internal/path/path_unix.go new file mode 100644 index 000000000..d5e589d3f --- /dev/null +++ b/updater/internal/path/path_unix.go @@ -0,0 +1,27 @@ +// Copyright observIQ, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build aix + +package path + +import "go.uber.org/zap" + +// UNIXInstallDir is the install directory of the collector on BSD Unix. +const UNIXInstallDir = "/opt/observiq-otel-collector" + +// InstallDir returns the filepath to the install directory +func InstallDir(_ *zap.Logger) (string, error) { + return UNIXInstallDir, nil +} diff --git a/updater/internal/service/service_aix.go b/updater/internal/service/service_aix.go new file mode 100644 index 000000000..2ab14ce1a --- /dev/null +++ b/updater/internal/service/service_aix.go @@ -0,0 +1,144 @@ +// Copyright observIQ, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build aix + +package service + +import ( + "fmt" + "os/exec" + + "go.uber.org/zap" +) + +const aixUnixServiceIdentifier = "oiqcollector" +const aixUnixServiceName = "observiq-otel-collector" + +// Option is an extra option for creating a Service +type Option func(aixUnixSvc *aixUnixService) + +// WithServiceFile returns an option setting the service file to use when updating using the service +func WithServiceFile(svcFilePath string) Option { + return func(aixUnixSvc *aixUnixService) { + // Do nothing + } +} + +// NewService returns an instance of the Service interface for managing the observiq-otel-collector service on the current OS. +func NewService(logger *zap.Logger, installDir string, opts ...Option) Service { + aixUnixSvc := &aixUnixService{ + serviceName: aixUnixServiceName, + serviceIdentifier: aixUnixServiceIdentifier, + installDir: installDir, + logger: logger.Named("aixUnix-service"), + } + + for _, opt := range opts { + opt(aixUnixSvc) + } + + return aixUnixSvc +} + +type aixUnixService struct { + // newServiceFilePath a useless stub to please service_action.go + newServiceFilePath string + // serviceName is the name of the service + serviceName string + // serviceName is the name of the service + serviceIdentifier string + installDir string + logger *zap.Logger +} + +// Start the service +func (l aixUnixService) Start() error { + // startsrc -s observiq-otel-collector -a start -e "$(cat /opt/observiq-otel-collector/observiq-otel-collector.env)" + //#nosec G204 -- serviceName is not determined by user input + cmd := exec.Command("startsrc", "-s", l.serviceName, "-a start -e \"$(cat /opt/observiq-otel-collector/observiq-otel-collector.env)\"") + if err := cmd.Run(); err != nil { + return fmt.Errorf("running service failed: %w", err) + } + return nil +} + +// Stop the service +func (l aixUnixService) Stop() error { + // stopsrc -s observiq-otel-collector + //#nosec G204 -- serviceName is not determined by user input + cmd := exec.Command("stopsrc", "-s", l.serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("stopping service failed: %w", err) + } + + return nil +} + +// installs the service +func (l aixUnixService) install() error { + // mkssys -s observiq-otel-collector -p /opt/observiq-otel-collector/observiq-otel-collector -u $(id -u observiq-otel-collector) -S -n15 -f9 -a '--config config.yaml --manager manager.yaml --logging logging.yaml' + //#nosec G204 -- serviceName is not determined by user input + cmd := exec.Command("mkssys", "-s", l.serviceName, "-p /opt/observiq-otel-collector/observiq-otel-collector -u $(id -u observiq-otel-collector) -S -n15 -f9 -a '--config config.yaml --manager manager.yaml --logging logging.yaml'") + if err := cmd.Run(); err != nil { + return fmt.Errorf("creating service file failed: %w", err) + } + // mkitab 'oiqcollector:23456789:respawn:startsrc -s observiq-otel-collector -a start -e "$(cat /opt/observiq-otel-collector/observiq-otel-collector.env)"' + //#nosec G204 -- serviceName is not determined by user input + cmd = exec.Command("mkitab", "'oiqcollector:23456789:respawn:startsrc -s", l.serviceName, "-a start -e \"$(cat /opt/observiq-otel-collector/observiq-otel-collector.env)\"'") + if err := cmd.Run(); err != nil { + return fmt.Errorf("enabling service file failed: %w", err) + } + + return nil +} + +// uninstalls the service +func (l aixUnixService) uninstall() error { + // Stop the service first + l.Stop() + + // rmitab oiqcollector + //#nosec G204 -- serviceIdentifier is not determined by user input + cmd := exec.Command("rmitab", l.serviceIdentifier) + if err := cmd.Run(); err != nil { + return fmt.Errorf("disabling service failed: %w", err) + } + + // rmssys -s observiq-otel-collector + //#nosec G204 -- serviceName is not determined by user input + cmd = exec.Command("rmssys", "-s", l.serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("removing service failed: %w", err) + } + + return nil +} + +func (l aixUnixService) Update() error { + if err := l.uninstall(); err != nil { + return fmt.Errorf("failed to uninstall old service: %w", err) + } + + if err := l.install(); err != nil { + return fmt.Errorf("failed to install new service: %w", err) + } + + return nil +} + +// No files, no backups +func (l aixUnixService) Backup() error { + return nil +} diff --git a/updater/internal/service/service_aix_test.go b/updater/internal/service/service_aix_test.go new file mode 100644 index 000000000..4a9675f4c --- /dev/null +++ b/updater/internal/service/service_aix_test.go @@ -0,0 +1,165 @@ +// Copyright observIQ, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// an elevated user is needed to run the service tests +//go:build aix && integration_aix + +package service + +import ( + "os/exec" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +// NOTE: These tests must run as root in order to pass +func TestaixUnixServiceInstall(t *testing.T) { + t.Run("Test install + uninstall", func(t *testing.T) { + uninstallService(t, "aix-service", "aix_svc") + + l := &aixUnixService{ + serviceName: "aix-service", + serviceIdentifier: "aix_svc", + logger: zaptest.NewLogger(t), + } + + err := l.install() + require.NoError(t, err) + + //We want to check that the service was actually loaded + requireServiceLoadedStatus(t, true) + + err = l.uninstall() + require.NoError(t, err) + + //Make sure the service is no longer listed + requireServiceLoadedStatus(t, false) + }) + + t.Run("Test start + stop", func(t *testing.T) { + uninstallService(t, "aix-service", "aix_svc") + + l := &aixUnixService{ + serviceName: "aix-service", + serviceIdentifier: "aix_svc", + logger: zaptest.NewLogger(t), + } + + err := l.install() + require.NoError(t, err) + + // We want to check that the service was actually loaded + requireServiceLoadedStatus(t, true) + + err = l.Start() + require.NoError(t, err) + + requireServiceRunningStatus(t, true) + + err = l.Stop() + require.NoError(t, err) + + requireServiceRunningStatus(t, false) + + err = l.uninstall() + require.NoError(t, err) + + // Make sure the service is no longer listed + requireServiceLoadedStatus(t, false) + }) + + t.Run("Uninstall fails if not installed", func(t *testing.T) { + uninstallService(t, "aix-service", "aix_svc") + + l := &aixUnixService{ + serviceName: "aix-service", + serviceIdentifier: "aix_svc", + logger: zaptest.NewLogger(t), + } + + err := l.uninstall() + require.ErrorContains(t, err, "disabling service failed") + requireServiceLoadedStatus(t, false) + }) + + t.Run("Start fails if service not found", func(t *testing.T) { + uninstallService(t, "aix-service", "aix_svc") + + l := &aixUnixService{ + serviceName: "aix-service", + serviceIdentifier: "aix_svc", + logger: zaptest.NewLogger(t), + } + + err := l.Start() + require.ErrorContains(t, err, "running service failed") + }) + + t.Run("Stop fails if service not found", func(t *testing.T) { + uninstallService(t, "aix-service", "aix_svc") + + l := &aixUnixService{ + serviceName: "aix-service", + serviceIdentifier: "aix_svc", + logger: zaptest.NewLogger(t), + } + + err := l.Stop() + require.ErrorContains(t, err, "stopping service failed") + }) +} + +// uninstallService is a helper that uninstalls the service manually for test setup, in case it is somehow leftover. +func uninstallService(t *testing.T, serviceName string, serviceIdentifier string) { + cmd := exec.Command("rmitab", serviceIdentifier) + cmd = exec.Command("rmssys", "-s", serviceName) +} + +const exitCodeServiceNotFound = 1 +const exitCodeServiceInactive = 0 + +func requireServiceLoadedStatus(t *testing.T, loaded bool) { + t.Helper() + + cmd := exec.Command("lssrc", "-s", "aix-service") + err := cmd.Run() + require.Error(t, err, "expected non-zero exit code from 'lssrc -s aix-service'") + + eErr, ok := err.(*exec.ExitError) + if loaded { + // If the service should be loaded, then we expect a 0 exit code, so no error is given + require.Equal(t, exitCodeServiceInactive, eErr.ExitCode(), "unexpected exit code when asserting service is loaded: %d", eErr.ExitCode()) + return + } + + require.True(t, ok, "systemctl status exited with non-ExitError: %s", eErr) + require.Equal(t, exitCodeServiceNotFound, eErr.ExitCode(), "unexpected exit code when asserting service is unloaded: %d", eErr.ExitCode()) +} + +func requireServiceRunningStatus(t *testing.T, running bool) { + cmd := exec.Command("systemctl", "status", "aix-service") + err := cmd.Run() + + if running { + // exit code 0 indicates service is loaded & running + require.NoError(t, err) + return + } + + eErr, ok := err.(*exec.ExitError) + require.True(t, ok, "systemctl status exited with non-ExitError: %s", eErr) + require.Equal(t, exitCodeServiceInactive, eErr.ExitCode(), "unexpected exit code when asserting service is not running: %d", eErr.ExitCode()) +}