From e5613d9a3051ba0d4cc76c366ffb7519422ca0ac Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Mon, 2 Oct 2023 14:57:41 -0600 Subject: [PATCH 01/26] Update Dockerfile and `magellan.sh` for container --- Dockerfile | 8 +++--- bin/magellan.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 7 deletions(-) mode change 100644 => 100755 bin/magellan.sh diff --git a/Dockerfile b/Dockerfile index f5d7b65..d5232d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,15 @@ FROM cgr.dev/chainguard/wolfi-base -RUN apk add --no-cache tini +RUN apk add --no-cache tini bash # nobody 65534:65534 USER 65534:65534 -COPY magellan / +COPY magellan /magellan +COPY bin/magellan.sh /usr/bin/magellan.sh -CMD [ "/magellan" ] + +CMD [ "/magellan.sh" ] ENTRYPOINT [ "/sbin/tini", "--" ] diff --git a/bin/magellan.sh b/bin/magellan.sh old mode 100644 new mode 100755 index 0850f87..8d1744d --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -1,17 +1,78 @@ +#!/bin/bash + +EXE=./magellan +SUBNETS="" +PORTS="" +USER="" +PASS="" +ARGS="" + function build(){ - go mod tidy && go build -C bin/magellan + go mod tidy && go build -C bin/magellan } function scan() { - ./magellan scan --subnet 172.16.0.0 --port 443 + # ./magellan scan --subnet 172.16.0.0 --port 443 + ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} } function list(){ - ./magellan list + # ./magellan list + ${EXE} list } function collect() { - ./magellan collect --user admin --pass password + # ./magellan collect --user admin --pass password + ${EXE} collect --user ${USER} --pass ${PASS} } + +# parse incoming arguments to set variables +while [[ $# -gt 0 ]]; do + case $1 in + --subnet) + SUBNETS="$2" + shift # past argument + shift # past value + ;; + -p|--port) + PORTS="$2" + shift # past argument + shift # past value + ;; + --user) + USER="$2" + shift # past argument + shift # past value + ;; + --pass|--password) + PASS="$2" + shift + shift + ;; + -*|--*) + echo "Unknown option $1" + exit 1 + ;; + *) + ARGS+=("$1") # save positional arg + shift # past argument + ;; + esac +done + +set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters + +echo "subnets = ${SUBNETS}" +echo "ports = ${PORTS}" +echo "user = ${USER}" +echo "pass = ..." + +if [[ -n $1 ]]; then + echo "Last line of file specified as non-opt/last argument:" + tail -1 "$1" +fi + +scan +collect \ No newline at end of file From b730e98fb3464f54d41783d9a386d6fe9d247708 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Mon, 2 Oct 2023 15:37:27 -0600 Subject: [PATCH 02/26] Fixed issue with `AddRedfishEndpoint` returning nil --- internal/api/smd/smd.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/api/smd/smd.go b/internal/api/smd/smd.go index a8369ea..76f518b 100644 --- a/internal/api/smd/smd.go +++ b/internal/api/smd/smd.go @@ -51,6 +51,9 @@ func AddRedfishEndpoint(data []byte, headers map[string]string) error { // Add redfish endpoint via POST `/hsm/v2/Inventory/RedfishEndpoints` endpoint url := makeEndpointUrl("/Inventory/RedfishEndpoints") res, body, err := util.MakeRequest(url, "POST", data, headers) + if res == nil { + return fmt.Errorf("no response") + } fmt.Printf("smd url: %v\n", url) fmt.Printf("res: %v\n", res.Status) fmt.Printf("body: %v\n", string(body)) @@ -69,6 +72,9 @@ func UpdateRedfishEndpoint(xname string, data []byte, headers map[string]string) // Update redfish endpoint via PUT `/hsm/v2/Inventory/RedfishEndpoints` endpoint url := makeEndpointUrl("/Inventory/RedfishEndpoints/" + xname) res, body, err := util.MakeRequest(url, "PUT", data, headers) + if res == nil { + return fmt.Errorf("no response") + } fmt.Printf("smd url: %v\n", url) fmt.Printf("res: %v\n", res.Status) fmt.Printf("body: %v\n", string(body)) From 7a99aa5b2dc906654833e40402997345d0764089 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Mon, 2 Oct 2023 15:42:06 -0600 Subject: [PATCH 03/26] Exposed more parameters in `magellan.sh` script --- bin/magellan.sh | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/bin/magellan.sh b/bin/magellan.sh index 8d1744d..585e457 100755 --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -5,6 +5,9 @@ SUBNETS="" PORTS="" USER="" PASS="" +SMD_HOST="" +THREADS="-1" +TIMEOUT="30" ARGS="" @@ -14,7 +17,7 @@ function build(){ function scan() { # ./magellan scan --subnet 172.16.0.0 --port 443 - ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} + ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} --host ${SMD_HOST} --timeout ${TIMEOUT} --threads ${THREADS} } function list(){ @@ -24,7 +27,7 @@ function list(){ function collect() { # ./magellan collect --user admin --pass password - ${EXE} collect --user ${USER} --pass ${PASS} + ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} } @@ -51,6 +54,21 @@ while [[ $# -gt 0 ]]; do shift shift ;; + --smd-host) + SMD_HOST="$2" + shift + shift + ;; + --timeout) + TIMEOUT="$2" + shift + shift + ;; + --threads) + THREADS="$2" + shift + shift + ;; -*|--*) echo "Unknown option $1" exit 1 From 69374a29e8dca30423cd1bc0c0b98fce007597e0 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 3 Oct 2023 12:21:52 -0600 Subject: [PATCH 04/26] Fixed argument passed to wrong command --- bin/magellan.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/magellan.sh b/bin/magellan.sh index 585e457..e81a90b 100755 --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -17,7 +17,7 @@ function build(){ function scan() { # ./magellan scan --subnet 172.16.0.0 --port 443 - ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} --host ${SMD_HOST} --timeout ${TIMEOUT} --threads ${THREADS} + ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} --timeout ${TIMEOUT} --threads ${THREADS} } function list(){ @@ -27,7 +27,7 @@ function list(){ function collect() { # ./magellan collect --user admin --pass password - ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} + ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} --host ${SMD_HOST} } From c2567a0b569b55d3ec226040710a3491aa0f9b12 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 3 Oct 2023 12:26:51 -0600 Subject: [PATCH 05/26] Added SMD port option for docker --- bin/magellan.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bin/magellan.sh b/bin/magellan.sh index e81a90b..4c34a82 100755 --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -6,6 +6,7 @@ PORTS="" USER="" PASS="" SMD_HOST="" +SMD_PORT="" THREADS="-1" TIMEOUT="30" ARGS="" @@ -27,7 +28,7 @@ function list(){ function collect() { # ./magellan collect --user admin --pass password - ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} --host ${SMD_HOST} + ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} --host ${SMD_HOST} --port ${SMD_PORT} } @@ -59,6 +60,11 @@ while [[ $# -gt 0 ]]; do shift shift ;; + --smd-port) + SMD_PORT="$2" + shift + shift + ;; --timeout) TIMEOUT="$2" shift @@ -82,6 +88,8 @@ done set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters +echo "SMD host = ${SMD_HOST}" +echo "SMD port = ${SMD_PORT}" echo "subnets = ${SUBNETS}" echo "ports = ${PORTS}" echo "user = ${USER}" From ce61aed9a4d682f55ee9c68ee61a6faac4d65b65 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Mon, 23 Oct 2023 16:12:18 -0400 Subject: [PATCH 06/26] Revised Dockerfile and goreleaser to allow for CI --- .goreleaser.yaml | 7 ++++++- Dockerfile | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a1c7c39..51724b1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -22,6 +22,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - "--label=org.opencontainers.image.version={{.Version}}" + extra_files: + - LICENSE + - CHANGELOG.md + - README.md + - bin/magellan.sh archives: - format: tar.gz rlcp: true @@ -34,10 +39,10 @@ archives: {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} files: - - migrations/* - LICENSE - CHANGELOG.md - README.md + - bin/magellan.sh checksum: name_template: 'checksums.txt' snapshot: diff --git a/Dockerfile b/Dockerfile index d5232d1..1e87c61 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ USER 65534:65534 COPY magellan /magellan -COPY bin/magellan.sh /usr/bin/magellan.sh +COPY /bin/magellan.sh /magellan.sh CMD [ "/magellan.sh" ] From 81d45ffad2f96ccb28572bcdeb67857257c16671 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Mon, 23 Oct 2023 18:24:03 -0400 Subject: [PATCH 07/26] Removing darwin to simplify release builds --- .goreleaser.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 51724b1..26482d3 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -3,11 +3,9 @@ before: - go mod download builds: - env: - - CGO_ENABLED=0 + - CGO_ENABLED=1 goos: - - darwin - linux - - windows goarch: - amd64 dockers: From b85aae84c3178c0e6f2a199f093e9c0c9822e4d0 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 08:04:14 -0600 Subject: [PATCH 08/26] Fixed HTTP response status code check for all successful codes --- internal/api/smd/smd.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/api/smd/smd.go b/internal/api/smd/smd.go index f257ad2..291ea79 100644 --- a/internal/api/smd/smd.go +++ b/internal/api/smd/smd.go @@ -5,7 +5,6 @@ package smd // https://github.com/alexlovelltroy/hms-smd import ( "fmt" - "net/http" "github.com/bikeshack/magellan/internal/util" // hms "github.com/alexlovelltroy/hms-smd" @@ -52,7 +51,8 @@ func AddRedfishEndpoint(data []byte, headers map[string]string) error { url := makeEndpointUrl("/Inventory/RedfishEndpoints") res, body, err := util.MakeRequest(url, "POST", data, headers) if res != nil { - if res.StatusCode != http.StatusOK { + statusOk := res.StatusCode >= 200 && res.StatusCode < 300 + if !statusOk { return fmt.Errorf("could not add redfish endpoint") } fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body)) @@ -69,7 +69,8 @@ func UpdateRedfishEndpoint(xname string, data []byte, headers map[string]string) res, body, err := util.MakeRequest(url, "PUT", data, headers) fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body)) if res != nil { - if res.StatusCode != http.StatusOK { + statusOk := res.StatusCode >= 200 && res.StatusCode < 300 + if !statusOk { return fmt.Errorf("could not update redfish endpoint") } } From 14447ee51e02d7fb0f092f7e550f4ad910ed1322 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 08:04:53 -0600 Subject: [PATCH 09/26] Formatted commands in `magellan.sh` script --- bin/magellan.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/bin/magellan.sh b/bin/magellan.sh index 4c34a82..3482337 100755 --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -7,9 +7,10 @@ USER="" PASS="" SMD_HOST="" SMD_PORT="" -THREADS="-1" +THREADS="1" TIMEOUT="30" ARGS="" +FORCE_UPDATE=false function build(){ @@ -18,7 +19,11 @@ function build(){ function scan() { # ./magellan scan --subnet 172.16.0.0 --port 443 - ${EXE} scan --subnet ${SUBNETS} --port ${PORTS} --timeout ${TIMEOUT} --threads ${THREADS} + ${EXE} scan \ + --subnet ${SUBNETS} \ + --port ${PORTS} \ + --timeout ${TIMEOUT} \ + --threads ${THREADS} } function list(){ @@ -28,7 +33,14 @@ function list(){ function collect() { # ./magellan collect --user admin --pass password - ${EXE} collect --user ${USER} --pass ${PASS} --timeout ${TIMEOUT} --threads ${THREADS} --host ${SMD_HOST} --port ${SMD_PORT} + ${EXE} collect \ + --user ${USER} \ + --pass ${PASS} \ + --timeout ${TIMEOUT} \ + --threads ${THREADS} \ + --host ${SMD_HOST} \ + --port ${SMD_PORT} \ + --force-update ${FORCE_UPDATE} } @@ -93,6 +105,8 @@ echo "SMD port = ${SMD_PORT}" echo "subnets = ${SUBNETS}" echo "ports = ${PORTS}" echo "user = ${USER}" +echo "threads = ${THREADS}" +echo "timeout = ${TIMEOUT}" echo "pass = ..." if [[ -n $1 ]]; then From 7a1c799910b2f3d3ac45b496099354ef3dbfe3fa Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 08:21:30 -0600 Subject: [PATCH 10/26] Changed `Collect*` function calls to reuse gofish client --- internal/collect.go | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/internal/collect.go b/internal/collect.go index 9e63253..10f6ae3 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -175,8 +175,13 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err json.Unmarshal(inventory, &rm) data["Inventory"] = rm["Inventory"] + c, err := connectGofish(q) + if err != nil { + l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) + } + // chassis - chassis, err := CollectChassis(q) + chassis, err := CollectChassis(c, q) if err != nil { l.Log.Errorf("could not query chassis: %v", err) continue @@ -212,7 +217,7 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err // data["Processors"] = rm["Processors"] // systems - systems, err := CollectSystems(client, q) + systems, err := CollectSystems(c, q) if err != nil { l.Log.Errorf("could not query systems: %v", err) } @@ -442,12 +447,7 @@ func CollectBios(client *bmclib.Client, q *QueryParams) ([]byte, error) { return b, err } -func CollectEthernetInterfaces(client *bmclib.Client, q *QueryParams, systemID string) ([]byte, error) { - c, err := connectGofish(q) - if err != nil { - return nil, fmt.Errorf("could not connect to bmc: %v", err) - } - +func CollectEthernetInterfaces(c *gofish.APIClient, q *QueryParams, systemID string) ([]byte, error) { systems, err := c.Service.Systems() if err != nil { return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err) @@ -478,11 +478,7 @@ func CollectEthernetInterfaces(client *bmclib.Client, q *QueryParams, systemID s return b, nil } -func CollectChassis(q *QueryParams) ([]byte, error) { - c, err := connectGofish(q) - if err != nil { - return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } +func CollectChassis(c *gofish.APIClient, q *QueryParams) ([]byte, error) { chassis, err := c.Service.Chassis() if err != nil { return nil, fmt.Errorf("could not query chassis (%v:%v): %v", q.Host, q.Port, err) @@ -500,12 +496,7 @@ func CollectChassis(q *QueryParams) ([]byte, error) { return b, nil } -func CollectStorage(q *QueryParams) ([]byte, error) { - c, err := connectGofish(q) - if err != nil { - return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } - +func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) { systems, err := c.Service.StorageSystems() if err != nil { return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err) @@ -533,12 +524,7 @@ func CollectStorage(q *QueryParams) ([]byte, error) { return b, nil } -func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) { - c, err := connectGofish(q) - if err != nil { - return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } - +func CollectSystems(c *gofish.APIClient, q *QueryParams) ([]byte, error) { systems, err := c.Service.Systems() if err != nil { return nil, fmt.Errorf("could not query systems (%v:%v): %v", q.Host, q.Port, err) @@ -547,7 +533,7 @@ func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) { // query the system's ethernet interfaces var temp []map[string]any for _, system := range systems { - interfaces, err := CollectEthernetInterfaces(client, q, system.ID) + interfaces, err := CollectEthernetInterfaces(c, q, system.ID) if err != nil { continue } @@ -571,12 +557,7 @@ func CollectSystems(client *bmclib.Client, q *QueryParams) ([]byte, error) { return b, nil } -func CollectRegisteries(q *QueryParams) ([]byte, error) { - c, err := connectGofish(q) - if err != nil { - return nil, fmt.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } - +func CollectRegisteries(c *gofish.APIClient, q *QueryParams) ([]byte, error) { registries, err := c.Service.Registries() if err != nil { return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err) From b610eda4d8ba1d408b132b8655ad5f9df7866818 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 09:20:48 -0600 Subject: [PATCH 11/26] Moved clients out of goroutine to be reused through collection process --- internal/collect.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/internal/collect.go b/internal/collect.go index 10f6ae3..67fb2de 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -130,6 +130,16 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err NodeBMC: -1, } + bmclibClient, err := NewClient(l, q) + if err != nil { + l.Log.Errorf("could not make client: %v", err) + } + + c, err := connectGofish(q) + if err != nil { + l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) + } + // collect bmc information asynchronously var wg sync.WaitGroup wg.Add(q.Threads) @@ -144,12 +154,6 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err q.Host = ps.Host q.Port = ps.Port - client, err := NewClient(l, q) - if err != nil { - l.Log.Errorf("could not make client: %v", err) - continue - } - node.NodeBMC += 1 // data to be sent to smd @@ -168,18 +172,13 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err var rm map[string]json.RawMessage // inventories - inventory, err := CollectInventory(client, q) + inventory, err := CollectInventory(bmclibClient, q) if err != nil { l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err) } json.Unmarshal(inventory, &rm) data["Inventory"] = rm["Inventory"] - c, err := connectGofish(q) - if err != nil { - l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } - // chassis chassis, err := CollectChassis(c, q) if err != nil { @@ -345,7 +344,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) { ctxCancel() return nil, fmt.Errorf("could not open client: %v", err) } - defer client.Close(ctx) inventory, err := client.Inventory(ctx) if err != nil { @@ -377,7 +375,6 @@ func CollectPowerState(client *bmclib.Client, q *QueryParams) ([]byte, error) { ctxCancel() return nil, fmt.Errorf("could not open client: %v", err) } - defer client.Close(ctx) powerState, err := client.GetPowerState(ctx) if err != nil { @@ -545,7 +542,7 @@ func CollectSystems(c *gofish.APIClient, q *QueryParams) ([]byte, error) { }) } - data := map[string]any{"Systems": temp } + data := map[string]any{"Systems": temp} b, err := json.MarshalIndent(data, "", " ") if err != nil { return nil, fmt.Errorf("could not marshal JSON: %v", err) From 6a59ba2a41f5c647b08c7364c3ce6ac58c80f07d Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 10:03:33 -0600 Subject: [PATCH 12/26] Moved clients back into goroutine and add checks --- internal/collect.go | 112 ++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/internal/collect.go b/internal/collect.go index 67fb2de..a8fb815 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -130,15 +130,6 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err NodeBMC: -1, } - bmclibClient, err := NewClient(l, q) - if err != nil { - l.Log.Errorf("could not make client: %v", err) - } - - c, err := connectGofish(q) - if err != nil { - l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) - } // collect bmc information asynchronously var wg sync.WaitGroup @@ -156,6 +147,16 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err node.NodeBMC += 1 + bmclibClient, err := NewClient(l, q) + if err != nil { + l.Log.Errorf("could not make client: %v", err) + } + + gofishClient, err := connectGofish(q) + if err != nil { + l.Log.Errorf("could not connect to bmc (%v:%v): %v", q.Host, q.Port, err) + } + // data to be sent to smd data := map[string]any{ "ID": fmt.Sprintf("%v", node.String()[:len(node.String())-2]), @@ -172,73 +173,40 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err var rm map[string]json.RawMessage // inventories - inventory, err := CollectInventory(bmclibClient, q) - if err != nil { - l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err) + if bmclibClient != nil { + inventory, err := CollectInventory(bmclibClient, q) + if err != nil { + l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err) + } + json.Unmarshal(inventory, &rm) + data["Inventory"] = rm["Inventory"] } - json.Unmarshal(inventory, &rm) - data["Inventory"] = rm["Inventory"] // chassis - chassis, err := CollectChassis(c, q) - if err != nil { - l.Log.Errorf("could not query chassis: %v", err) - continue + if gofishClient != nil { + chassis, err := CollectChassis(gofishClient, q) + if err != nil { + l.Log.Errorf("could not query chassis: %v", err) + continue + } + json.Unmarshal(chassis, &rm) + data["Chassis"] = rm["Chassis"] + + // systems + systems, err := CollectSystems(gofishClient, q) + if err != nil { + l.Log.Errorf("could not query systems: %v", err) + } + json.Unmarshal(systems, &rm) + data["Systems"] = rm["Systems"] + + // add other fields from systems + if len(rm["Systems"]) > 0 { + var s map[string][]interface{} + json.Unmarshal(rm["Systems"], &s) + data["Name"] = s["Name"] + } } - json.Unmarshal(chassis, &rm) - data["Chassis"] = rm["Chassis"] - - // ethernet interfaces - // interfaces, err := QueryEthernetInterfaces(client, q) - // if err != nil { - // l.Log.Errorf("could not query ethernet interfaces: %v", err) - // continue - // } - // json.Unmarshal(interfaces, &rm) - // data["Interfaces"] = rm["Interfaces"] - - // storage - // storage, err := QueryStorage(q) - // if err != nil { - // l.Log.Errorf("could not query storage: %v", err) - // continue - // } - // json.Unmarshal(storage, &rm) - // data["Storage"] = rm["Storage"] - - // get specific processor info - // procs, err := QueryProcessors(q) - // if err != nil { - // l.Log.Errorf("could not query processors: %v", err) - // } - // var p map[string]interface{} - // json.Unmarshal(procs, &p) - // data["Processors"] = rm["Processors"] - - // systems - systems, err := CollectSystems(c, q) - if err != nil { - l.Log.Errorf("could not query systems: %v", err) - } - json.Unmarshal(systems, &rm) - data["Systems"] = rm["Systems"] - - // add other fields from systems - if len(rm["Systems"]) > 0 { - var s map[string][]interface{} - json.Unmarshal(rm["Systems"], &s) - data["Name"] = s["Name"] - } - - // data["Type"] = rm[""] - - // registries - // registries, err := QueryRegisteries(q) - // if err != nil { - // l.Log.Errorf("could not query registries: %v", err) - // } - // json.Unmarshal(registries, &rm) - // data["Registries"] = rm["Registries"] headers := make(map[string]string) headers["Content-Type"] = "application/json" From e0390992638f5fb7ffa2e5c8b853fc268d7a1169 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 24 Oct 2023 14:33:32 -0600 Subject: [PATCH 13/26] Changed how arguments are passed to `magellan.sh` script --- bin/magellan.sh | 54 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/bin/magellan.sh b/bin/magellan.sh index 3482337..98b3a37 100755 --- a/bin/magellan.sh +++ b/bin/magellan.sh @@ -11,6 +11,8 @@ THREADS="1" TIMEOUT="30" ARGS="" FORCE_UPDATE=false +SCAN_PARAMS="" +COLLECT_PARAMS="" function build(){ @@ -19,11 +21,11 @@ function build(){ function scan() { # ./magellan scan --subnet 172.16.0.0 --port 443 - ${EXE} scan \ - --subnet ${SUBNETS} \ - --port ${PORTS} \ - --timeout ${TIMEOUT} \ - --threads ${THREADS} + ${EXE} scan ${SCAN_PARAMS} + # --subnet ${SUBNETS} \ + # --port ${PORTS} \ + # --timeout ${TIMEOUT} \ + # --threads ${THREADS} } function list(){ @@ -33,20 +35,30 @@ function list(){ function collect() { # ./magellan collect --user admin --pass password - ${EXE} collect \ - --user ${USER} \ - --pass ${PASS} \ - --timeout ${TIMEOUT} \ - --threads ${THREADS} \ - --host ${SMD_HOST} \ - --port ${SMD_PORT} \ - --force-update ${FORCE_UPDATE} + ${EXE} collect ${COLLECT_PARAMS} + # --user ${USER} \ + # --pass ${PASS} \ + # --timeout ${TIMEOUT} \ + # --threads ${THREADS} \ + # --host ${SMD_HOST} \ + # --port ${SMD_PORT} \ + # --force-update ${FORCE_UPDATE} } # parse incoming arguments to set variables while [[ $# -gt 0 ]]; do case $1 in + --scan) + SCAN_PARAMS="$2" + shift + shift + ;; + --collect) + COLLECT_PARAMS="$2" + shift + shift + ;; --subnet) SUBNETS="$2" shift # past argument @@ -100,19 +112,15 @@ done set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters -echo "SMD host = ${SMD_HOST}" -echo "SMD port = ${SMD_PORT}" -echo "subnets = ${SUBNETS}" -echo "ports = ${PORTS}" -echo "user = ${USER}" -echo "threads = ${THREADS}" -echo "timeout = ${TIMEOUT}" -echo "pass = ..." - if [[ -n $1 ]]; then echo "Last line of file specified as non-opt/last argument:" tail -1 "$1" fi scan -collect \ No newline at end of file +collect + +# run with docker +# docker run magellan:latest magellan.sh \ +# --scan "--subnet 127.16.0.0 --port 443" \ +# --collect "--user admin --pass password --timeout 300 --threads 1 --smd-host host --smd-port port" \ No newline at end of file From 47f773df2a6ca0977c383adec5465be3894ecc64 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Wed, 25 Oct 2023 12:13:21 -0600 Subject: [PATCH 14/26] Changed how the data is outputted to stdout and fixed xname issue --- internal/collect.go | 93 +++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/internal/collect.go b/internal/collect.go index a8fb815..5c7ffb7 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -121,17 +121,9 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err found := make([]string, 0, len(*probeStates)) done := make(chan struct{}, q.Threads+1) chanProbeState := make(chan ScannedResult, q.Threads+1) - - // generate custom xnames for bmcs - node := xnames.Node{ - Cabinet: 1000, - Chassis: 1, - ComputeModule: 7, - NodeBMC: -1, - } - - + // collect bmc information asynchronously + var offset = 0 var wg sync.WaitGroup wg.Add(q.Threads) for i := 0; i < q.Threads; i++ { @@ -144,13 +136,20 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err } q.Host = ps.Host q.Port = ps.Port - - node.NodeBMC += 1 - - bmclibClient, err := NewClient(l, q) - if err != nil { - l.Log.Errorf("could not make client: %v", err) + + // generate custom xnames for bmcs + node := xnames.Node{ + Cabinet: 1000, + Chassis: 1, + ComputeModule: 7, + NodeBMC: offset, } + offset += 1 + + // bmclibClient, err := NewClient(l, q) + // if err != nil { + // l.Log.Errorf("could not make client: %v", err) + // } gofishClient, err := connectGofish(q) if err != nil { @@ -173,14 +172,14 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err var rm map[string]json.RawMessage // inventories - if bmclibClient != nil { - inventory, err := CollectInventory(bmclibClient, q) - if err != nil { - l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err) - } - json.Unmarshal(inventory, &rm) - data["Inventory"] = rm["Inventory"] - } + // if bmclibClient != nil { + // inventory, err := CollectInventory(bmclibClient, q) + // if err != nil { + // l.Log.Errorf("could not query inventory (%v:%v): %v", q.Host, q.Port, err) + // } + // json.Unmarshal(inventory, &rm) + // data["Inventory"] = rm["Inventory"] + // } // chassis if gofishClient != nil { @@ -211,25 +210,29 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err headers := make(map[string]string) headers["Content-Type"] = "application/json" - b, err := json.MarshalIndent(data, "", " ") + body, err := json.MarshalIndent(data, "", " ") if err != nil { l.Log.Errorf("could not marshal JSON: %v", err) } + if q.Verbose { + fmt.Printf("%v\n", string(body)) + } + // write JSON data to file - err = os.WriteFile(path.Clean(outputPath + "/" + q.Host + ".json"), b, os.ModePerm) + err = os.WriteFile(path.Clean(outputPath + "/" + q.Host + ".json"), body, os.ModePerm) if err != nil { l.Log.Errorf("could not write data to file: %v", err) } // add all endpoints to smd - err = smd.AddRedfishEndpoint(b, headers) + err = smd.AddRedfishEndpoint(body, headers) if err != nil { l.Log.Error(err) // try updating instead if q.ForceUpdate { - err = smd.UpdateRedfishEndpoint(data["ID"].(string), b, headers) + err = smd.UpdateRedfishEndpoint(data["ID"].(string), body, headers) if err != nil { l.Log.Error(err) } @@ -296,9 +299,6 @@ func CollectMetadata(client *bmclib.Client, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("Metadata: %v\n", string(b)) - } ctxCancel() return b, nil } @@ -328,9 +328,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } ctxCancel() return b, nil } @@ -358,9 +355,6 @@ func CollectPowerState(client *bmclib.Client, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } ctxCancel() return b, nil @@ -392,11 +386,7 @@ func CollectUsers(client *bmclib.Client, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - // return b, nil ctxCancel() - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } @@ -406,9 +396,6 @@ func CollectBios(client *bmclib.Client, q *QueryParams) ([]byte, error) { // return nil, fmt.Errorf("could not make query: %v", err) // } b, err := makeRequest(client, client.GetBiosConfiguration, q.Timeout) - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, err } @@ -437,9 +424,6 @@ func CollectEthernetInterfaces(c *gofish.APIClient, q *QueryParams, systemID str return nil, fmt.Errorf("could not marshal JSON: %v", err) } - // if q.Verbose { - // fmt.Printf("%v\n", string(b)) - // } return b, nil } @@ -455,9 +439,6 @@ func CollectChassis(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } @@ -483,9 +464,6 @@ func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } @@ -516,9 +494,6 @@ func CollectSystems(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } @@ -534,9 +509,6 @@ func CollectRegisteries(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } @@ -577,9 +549,6 @@ func CollectProcessors(q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not marshal JSON: %v", err) } - if q.Verbose { - fmt.Printf("%v\n", string(b)) - } return b, nil } From c5611f598f63dfb085ead7978d713fa22eaf3829 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Wed, 25 Oct 2023 12:13:53 -0600 Subject: [PATCH 15/26] Changed the error message when attempting to add redfish endpoint --- internal/api/smd/smd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/smd/smd.go b/internal/api/smd/smd.go index 291ea79..40bcdec 100644 --- a/internal/api/smd/smd.go +++ b/internal/api/smd/smd.go @@ -53,7 +53,7 @@ func AddRedfishEndpoint(data []byte, headers map[string]string) error { if res != nil { statusOk := res.StatusCode >= 200 && res.StatusCode < 300 if !statusOk { - return fmt.Errorf("could not add redfish endpoint") + return fmt.Errorf("returned status code %d when adding endpoint", res.StatusCode) } fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body)) } From 2af5ed651aa892de8a4440daace32f93503604b2 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Wed, 25 Oct 2023 13:17:12 -0600 Subject: [PATCH 16/26] Updated the `README.md` with Docker info --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 124f80a..d8f8d06 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ go mod tidy && go build This should find and download all of the required dependencies. Although other versions of Go may work, the project has only been tested with v1.20. +To build the Docker container, run `docker build -t magellan:latest .` in the +project's directory. + ## Usage There are three main commands to use with the tool: `scan`, `list`, and `collect`. @@ -81,6 +84,13 @@ to find the `hms-smd` API. Note: If the `db.path` flag is not set, `magellan` will use /tmp/magellan.db by default. +Both the `scan` and `collect` commands can be ran via Docker after pulling the image: + +```bash +docker pull bikeshack/magellan:latest +docker run bikeshack/magellan:latest /magellan.sh --scan "--subnet 172.16.0.0 --port 443 --timeout 3" --collect "--user admin --pass password --host http://vm01 --port 27779" +``` + ## TODO List of things left to fix, do, or ideas... @@ -89,9 +99,9 @@ List of things left to fix, do, or ideas... * [ ] Set default port automatically depending on the driver used to scan * [X] Test using different `bmclib` supported drivers (mainly 'redfish') * [X] Confirm loading different components into `hms-smd` -* [ ] Add ability to set subnet mask for scanning +* [X] Add ability to set subnet mask for scanning * [ ] Add unit tests for `scan`, `list`, and `collect` commands -* [ ] Clean up, remove unused, and tidy code +* [X] Clean up, remove unused, and tidy code ## Copyright From 955c9fb4bd4fd60aebb6f5a58266f547e99cdd44 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Tue, 31 Oct 2023 07:55:03 -0600 Subject: [PATCH 17/26] Change import to use new repo --- cmd/collect.go | 8 ++++---- cmd/list.go | 2 +- cmd/scan.go | 4 ++-- cmd/update.go | 4 ++-- go.mod | 2 +- internal/api/dora/dora.go | 2 +- internal/api/smd/smd.go | 2 +- internal/collect.go | 6 +++--- internal/db/sqlite/sqlite.go | 2 +- internal/scan.go | 2 +- internal/update.go | 4 ++-- main.go | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cmd/collect.go b/cmd/collect.go index 93d54a3..273de4a 100644 --- a/cmd/collect.go +++ b/cmd/collect.go @@ -1,10 +1,10 @@ package cmd import ( - magellan "github.com/bikeshack/magellan/internal" - "github.com/bikeshack/magellan/internal/api/smd" - "github.com/bikeshack/magellan/internal/db/sqlite" - "github.com/bikeshack/magellan/internal/log" + magellan "github.com/OpenChami/magellan/internal" + "github.com/OpenChami/magellan/internal/api/smd" + "github.com/OpenChami/magellan/internal/db/sqlite" + "github.com/OpenChami/magellan/internal/log" "github.com/cznic/mathutil" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/list.go b/cmd/list.go index 3e344a0..655aea8 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/bikeshack/magellan/internal/db/sqlite" + "github.com/OpenChami/magellan/internal/db/sqlite" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/scan.go b/cmd/scan.go index 5f4ec53..02c366f 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -6,8 +6,8 @@ import ( "os" "path" - magellan "github.com/bikeshack/magellan/internal" - "github.com/bikeshack/magellan/internal/db/sqlite" + magellan "github.com/OpenChami/magellan/internal" + "github.com/OpenChami/magellan/internal/db/sqlite" "github.com/cznic/mathutil" "github.com/spf13/cobra" diff --git a/cmd/update.go b/cmd/update.go index 48dc6b1..ce2e4da 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -1,8 +1,8 @@ package cmd import ( - magellan "github.com/bikeshack/magellan/internal" - "github.com/bikeshack/magellan/internal/log" + magellan "github.com/OpenChami/magellan/internal" + "github.com/OpenChami/magellan/internal/log" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) diff --git a/go.mod b/go.mod index 11f71c8..3f4f136 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/bikeshack/magellan +module github.com/OpenChami/magellan go 1.20 diff --git a/internal/api/dora/dora.go b/internal/api/dora/dora.go index e1a27f9..880fc4a 100644 --- a/internal/api/dora/dora.go +++ b/internal/api/dora/dora.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/bikeshack/magellan/internal/util" + "github.com/OpenChami/magellan/internal/util" "github.com/jmoiron/sqlx" ) diff --git a/internal/api/smd/smd.go b/internal/api/smd/smd.go index 40bcdec..a3e9f66 100644 --- a/internal/api/smd/smd.go +++ b/internal/api/smd/smd.go @@ -6,7 +6,7 @@ package smd import ( "fmt" - "github.com/bikeshack/magellan/internal/util" + "github.com/OpenChami/magellan/internal/util" // hms "github.com/alexlovelltroy/hms-smd" ) diff --git a/internal/collect.go b/internal/collect.go index 5c7ffb7..4a0e0cb 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -12,10 +12,10 @@ import ( "sync" "time" - "github.com/bikeshack/magellan/internal/log" + "github.com/OpenChami/magellan/internal/log" - "github.com/bikeshack/magellan/internal/api/smd" - "github.com/bikeshack/magellan/internal/util" + "github.com/OpenChami/magellan/internal/api/smd" + "github.com/OpenChami/magellan/internal/util" "github.com/Cray-HPE/hms-xname/xnames" bmclib "github.com/bmc-toolbox/bmclib/v2" diff --git a/internal/db/sqlite/sqlite.go b/internal/db/sqlite/sqlite.go index 7665f71..2e4211e 100644 --- a/internal/db/sqlite/sqlite.go +++ b/internal/db/sqlite/sqlite.go @@ -3,7 +3,7 @@ package sqlite import ( "fmt" - magellan "github.com/bikeshack/magellan/internal" + magellan "github.com/OpenChami/magellan/internal" "github.com/jmoiron/sqlx" ) diff --git a/internal/scan.go b/internal/scan.go index d30a51a..ca2729e 100644 --- a/internal/scan.go +++ b/internal/scan.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/bikeshack/magellan/internal/util" + "github.com/OpenChami/magellan/internal/util" ) type ScannedResult struct { diff --git a/internal/update.go b/internal/update.go index 312eeb8..3b352ec 100644 --- a/internal/update.go +++ b/internal/update.go @@ -10,8 +10,8 @@ import ( "strings" "time" - "github.com/bikeshack/magellan/internal/log" - "github.com/bikeshack/magellan/internal/util" + "github.com/OpenChami/magellan/internal/log" + "github.com/OpenChami/magellan/internal/util" bmclib "github.com/bmc-toolbox/bmclib/v2" "github.com/bmc-toolbox/bmclib/v2/constants" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" diff --git a/main.go b/main.go index 2cfda3a..6f5195a 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/bikeshack/magellan/cmd" + "github.com/OpenChami/magellan/cmd" ) func main() { From cccf0dfdf203a3f47aa3caa896023139cd10039a Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Fri, 3 Nov 2023 09:47:57 -0400 Subject: [PATCH 18/26] move to OpenCHAMI and update changelog --- .goreleaser.yaml | 8 +++--- CHANGELOG.md | 13 +++++++++- cmd/collect.go | 8 +++--- cmd/list.go | 2 +- cmd/scan.go | 16 ++++++------ cmd/update.go | 48 ++++++++++++++++++------------------ go.mod | 2 +- internal/api/dora/dora.go | 2 +- internal/api/smd/smd.go | 2 +- internal/collect.go | 46 +++++++++++++++++----------------- internal/db/sqlite/sqlite.go | 2 +- internal/scan.go | 7 +++--- internal/update.go | 29 ++++++++++------------ main.go | 2 +- 14 files changed, 96 insertions(+), 91 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 26482d3..8a3dd5d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -10,10 +10,10 @@ builds: - amd64 dockers: - image_templates: - - bikeshack/{{.ProjectName}}:latest - - bikeshack/{{.ProjectName}}:{{ .Tag }} - - bikeshack/{{.ProjectName}}:{{ .Major }} - - bikeshack/{{.ProjectName}}:{{ .Major }}.{{ .Minor }} + - ghcr.io/OpenCHAMI/{{.ProjectName}}:latest + - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Tag }} + - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Major }} + - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Major }}.{{ .Minor }} build_flag_templates: - "--pull" - "--label=org.opencontainers.image.created={{.Date}}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 701718f..f999cdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] + + +## [0.0.5] - 2023-11-02 + +### Added + + * Ability to update firmware + * Refactored connection handling for faster scanning + * Updated to refelct home at github.com/OpenCHAMI + * Updated to reflect ghcr.io as container home + + ## [Unreleased] ## [0.0.1] - 2023-09-14 diff --git a/cmd/collect.go b/cmd/collect.go index 273de4a..e38cfe0 100644 --- a/cmd/collect.go +++ b/cmd/collect.go @@ -1,10 +1,10 @@ package cmd import ( - magellan "github.com/OpenChami/magellan/internal" - "github.com/OpenChami/magellan/internal/api/smd" - "github.com/OpenChami/magellan/internal/db/sqlite" - "github.com/OpenChami/magellan/internal/log" + magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/OpenCHAMI/magellan/internal/api/smd" + "github.com/OpenCHAMI/magellan/internal/db/sqlite" + "github.com/OpenCHAMI/magellan/internal/log" "github.com/cznic/mathutil" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/list.go b/cmd/list.go index 655aea8..9ad54bb 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/OpenChami/magellan/internal/db/sqlite" + "github.com/OpenCHAMI/magellan/internal/db/sqlite" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/scan.go b/cmd/scan.go index 02c366f..127b11e 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -6,18 +6,18 @@ import ( "os" "path" - magellan "github.com/OpenChami/magellan/internal" - "github.com/OpenChami/magellan/internal/db/sqlite" + magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/OpenCHAMI/magellan/internal/db/sqlite" "github.com/cznic/mathutil" "github.com/spf13/cobra" ) var ( - begin uint8 - end uint8 - subnets []string - subnetMasks []net.IP + begin uint8 + end uint8 + subnets []string + subnetMasks []net.IP disableProbing bool ) @@ -35,10 +35,10 @@ var scanCmd = &cobra.Command{ return } - if len(subnetMasks) < i + 1 { + if len(subnetMasks) < i+1 { subnetMasks = append(subnetMasks, net.IP{255, 255, 255, 0}) } - + hostsToScan = append(hostsToScan, magellan.GenerateHosts(subnet, &subnetMasks[i])...) } } diff --git a/cmd/update.go b/cmd/update.go index ce2e4da..94ccd71 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -1,45 +1,45 @@ package cmd import ( - magellan "github.com/OpenChami/magellan/internal" - "github.com/OpenChami/magellan/internal/log" + magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/OpenCHAMI/magellan/internal/log" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( - host string - port int - firmwareUrl string - firmwareVersion string - component string + host string + port int + firmwareUrl string + firmwareVersion string + component string transferProtocol string - status bool + status bool ) var updateCmd = &cobra.Command{ - Use: "update", + Use: "update", Short: "Update BMC node firmware", Run: func(cmd *cobra.Command, args []string) { l := log.NewLogger(logrus.New(), logrus.DebugLevel) - q := &magellan.UpdateParams { - FirmwarePath: firmwareUrl, - FirmwareVersion: firmwareVersion, - Component: component, + q := &magellan.UpdateParams{ + FirmwarePath: firmwareUrl, + FirmwareVersion: firmwareVersion, + Component: component, TransferProtocol: transferProtocol, QueryParams: magellan.QueryParams{ - Drivers: []string{"redfish"}, - Preferred: "redfish", - Protocol: protocol, - Host: host, - User: user, - Pass: pass, - Timeout: timeout, - Port: port, + Drivers: []string{"redfish"}, + Preferred: "redfish", + Protocol: protocol, + Host: host, + User: user, + Pass: pass, + Timeout: timeout, + Port: port, WithSecureTLS: withSecureTLS, }, } - + // check if required params are set if host == "" || user == "" || pass == "" { l.Log.Fatal("requires host, user, and pass to be set") @@ -53,7 +53,7 @@ var updateCmd = &cobra.Command{ } return } - + // client, err := magellan.NewClient(l, &q.QueryParams) // if err != nil { // l.Log.Errorf("could not make client: %v", err) @@ -79,4 +79,4 @@ func init() { updateCmd.Flags().BoolVar(&withSecureTLS, "secure-tls", false, "enable secure TLS") updateCmd.Flags().BoolVar(&status, "status", false, "get the status of the update") rootCmd.AddCommand(updateCmd) -} \ No newline at end of file +} diff --git a/go.mod b/go.mod index 3f4f136..1b5ea7b 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/OpenChami/magellan +module github.com/OpenCHAMI/magellan go 1.20 diff --git a/internal/api/dora/dora.go b/internal/api/dora/dora.go index 880fc4a..880ac7b 100644 --- a/internal/api/dora/dora.go +++ b/internal/api/dora/dora.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/OpenChami/magellan/internal/util" + "github.com/OpenCHAMI/magellan/internal/util" "github.com/jmoiron/sqlx" ) diff --git a/internal/api/smd/smd.go b/internal/api/smd/smd.go index a3e9f66..ca0a606 100644 --- a/internal/api/smd/smd.go +++ b/internal/api/smd/smd.go @@ -6,7 +6,7 @@ package smd import ( "fmt" - "github.com/OpenChami/magellan/internal/util" + "github.com/OpenCHAMI/magellan/internal/util" // hms "github.com/alexlovelltroy/hms-smd" ) diff --git a/internal/collect.go b/internal/collect.go index 4a0e0cb..b00ee63 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -12,10 +12,10 @@ import ( "sync" "time" - "github.com/OpenChami/magellan/internal/log" + "github.com/OpenCHAMI/magellan/internal/log" - "github.com/OpenChami/magellan/internal/api/smd" - "github.com/OpenChami/magellan/internal/util" + "github.com/OpenCHAMI/magellan/internal/api/smd" + "github.com/OpenCHAMI/magellan/internal/util" "github.com/Cray-HPE/hms-xname/xnames" bmclib "github.com/bmc-toolbox/bmclib/v2" @@ -33,7 +33,6 @@ const ( HTTPS_PORT = 443 ) - // NOTE: ...params were getting too long... type QueryParams struct { Host string @@ -121,7 +120,7 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err found := make([]string, 0, len(*probeStates)) done := make(chan struct{}, q.Threads+1) chanProbeState := make(chan ScannedResult, q.Threads+1) - + // collect bmc information asynchronously var offset = 0 var wg sync.WaitGroup @@ -136,7 +135,7 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err } q.Host = ps.Host q.Port = ps.Port - + // generate custom xnames for bmcs node := xnames.Node{ Cabinet: 1000, @@ -158,13 +157,13 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err // data to be sent to smd data := map[string]any{ - "ID": fmt.Sprintf("%v", node.String()[:len(node.String())-2]), - "Type": "", - "Name": "", - "FQDN": ps.Host, - "User": q.User, - "Password": q.Pass, - "MACRequired": true, + "ID": fmt.Sprintf("%v", node.String()[:len(node.String())-2]), + "Type": "", + "Name": "", + "FQDN": ps.Host, + "User": q.User, + "Password": q.Pass, + "MACRequired": true, "RediscoverOnUpdate": false, } @@ -220,7 +219,7 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err } // write JSON data to file - err = os.WriteFile(path.Clean(outputPath + "/" + q.Host + ".json"), body, os.ModePerm) + err = os.WriteFile(path.Clean(outputPath+"/"+q.Host+".json"), body, os.ModePerm) if err != nil { l.Log.Errorf("could not write data to file: %v", err) } @@ -319,7 +318,6 @@ func CollectInventory(client *bmclib.Client, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not get inventory: %v", err) } - // retrieve inventory data data := map[string]any{"Inventory": inventory} b, err := json.MarshalIndent(data, "", " ") @@ -379,7 +377,7 @@ func CollectUsers(client *bmclib.Client, q *QueryParams) ([]byte, error) { } // retrieve inventory data - data := map[string]any {"Users": users} + data := map[string]any{"Users": users} b, err := json.MarshalIndent(data, "", " ") if err != nil { ctxCancel() @@ -407,13 +405,13 @@ func CollectEthernetInterfaces(c *gofish.APIClient, q *QueryParams, systemID str var interfaces []*redfish.EthernetInterface for _, system := range systems { - i, err := redfish.ListReferencedEthernetInterfaces(c, "/redfish/v1/Systems/" + system.ID + "/EthernetInterfaces/") + i, err := redfish.ListReferencedEthernetInterfaces(c, "/redfish/v1/Systems/"+system.ID+"/EthernetInterfaces/") if err != nil { continue } interfaces = append(interfaces, i...) } - + if len(interfaces) <= 0 { return nil, fmt.Errorf("could not get ethernet interfaces: %v", err) } @@ -442,7 +440,7 @@ func CollectChassis(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return b, nil } -func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) { +func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) { systems, err := c.Service.StorageSystems() if err != nil { return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err) @@ -455,7 +453,7 @@ func CollectStorage(c *gofish.APIClient, q *QueryParams) ([]byte, error) { data := map[string]any{ "Storage": map[string]any{ - "Systems": systems, + "Systems": systems, "Services": services, }, } @@ -483,7 +481,7 @@ func CollectSystems(c *gofish.APIClient, q *QueryParams) ([]byte, error) { var i map[string]any json.Unmarshal(interfaces, &i) temp = append(temp, map[string]any{ - "Data": system, + "Data": system, "EthernetInterfaces": i["EthernetInterfaces"], }) } @@ -503,7 +501,7 @@ func CollectRegisteries(c *gofish.APIClient, q *QueryParams) ([]byte, error) { return nil, fmt.Errorf("could not query storage systems (%v:%v): %v", q.Host, q.Port, err) } - data := map[string]any{"Registries": registries } + data := map[string]any{"Registries": registries} b, err := json.MarshalIndent(data, "", " ") if err != nil { return nil, fmt.Errorf("could not marshal JSON: %v", err) @@ -543,7 +541,7 @@ func CollectProcessors(q *QueryParams) ([]byte, error) { } } - data := map[string]any{"Processors": procs } + data := map[string]any{"Processors": procs} b, err := json.MarshalIndent(data, "", " ") if err != nil { return nil, fmt.Errorf("could not marshal JSON: %v", err) @@ -563,7 +561,7 @@ func connectGofish(q *QueryParams) (*gofish.APIClient, error) { c.Service.ProtocolFeaturesSupported = gofish.ProtocolFeaturesSupported{ ExpandQuery: gofish.Expand{ ExpandAll: true, - Links: true, + Links: true, }, } } diff --git a/internal/db/sqlite/sqlite.go b/internal/db/sqlite/sqlite.go index 2e4211e..73f0376 100644 --- a/internal/db/sqlite/sqlite.go +++ b/internal/db/sqlite/sqlite.go @@ -3,7 +3,7 @@ package sqlite import ( "fmt" - magellan "github.com/OpenChami/magellan/internal" + magellan "github.com/OpenCHAMI/magellan/internal" "github.com/jmoiron/sqlx" ) diff --git a/internal/scan.go b/internal/scan.go index ca2729e..68e8f4f 100644 --- a/internal/scan.go +++ b/internal/scan.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/OpenChami/magellan/internal/util" + "github.com/OpenCHAMI/magellan/internal/util" ) type ScannedResult struct { @@ -51,7 +51,6 @@ func rawConnect(host string, ports []int, timeout int, keepOpenOnly bool) []Scan return results } - func GenerateHosts(subnet string, subnetMask *net.IP) []string { if subnet == "" || subnetMask == nil { return nil @@ -68,7 +67,7 @@ func GenerateHosts(subnet string, subnetMask *net.IP) []string { subnetIp = ip if network != nil { t := net.IP(network.Mask) - subnetMask = &t + subnetMask = &t } } @@ -82,7 +81,7 @@ func generateHosts(ip *net.IP, mask *net.IPMask) []string { // get all IP addresses in network ones, _ := mask.Size() hosts := []string{} - end := int(math.Pow(2, float64((32-ones))))-1 + end := int(math.Pow(2, float64((32-ones)))) - 1 for i := 0; i < end; i++ { // ip[3] = byte(i) ip = util.GetNextIP(ip, 1) diff --git a/internal/update.go b/internal/update.go index 3b352ec..ce5c544 100644 --- a/internal/update.go +++ b/internal/update.go @@ -10,30 +10,29 @@ import ( "strings" "time" - "github.com/OpenChami/magellan/internal/log" - "github.com/OpenChami/magellan/internal/util" + "github.com/OpenCHAMI/magellan/internal/log" + "github.com/OpenCHAMI/magellan/internal/util" bmclib "github.com/bmc-toolbox/bmclib/v2" "github.com/bmc-toolbox/bmclib/v2/constants" bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors" "github.com/sirupsen/logrus" ) - type UpdateParams struct { QueryParams - FirmwarePath string - FirmwareVersion string - Component string + FirmwarePath string + FirmwareVersion string + Component string TransferProtocol string } -// NOTE: Does not work since OpenBMC, whic bmclib uses underneath, does not +// NOTE: Does not work since OpenBMC, whic bmclib uses underneath, does not // support multipart updates. See issue: https://github.com/bmc-toolbox/bmclib/issues/341 func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error { if q.Component == "" { return fmt.Errorf("component is required") } - + // open BMC session and update driver registry ctx, ctxCancel := context.WithTimeout(context.Background(), time.Second*time.Duration(q.Timeout)) client.Registry.FilterForCompatible(ctx) @@ -121,20 +120,20 @@ func UpdateFirmware(client *bmclib.Client, l *log.Logger, q *UpdateParams) error time.Sleep(2 * time.Second) } - + return nil } func UpdateFirmwareRemote(q *UpdateParams) error { url := baseRedfishUrl(&q.QueryParams) + "/redfish/v1/UpdateService/Actions/SimpleUpdate" - headers := map[string]string { - "Content-Type": "application/json", + headers := map[string]string{ + "Content-Type": "application/json", "cache-control": "no-cache", } b := map[string]any{ - "UpdateComponent": q.Component, // BMC, BIOS + "UpdateComponent": q.Component, // BMC, BIOS "TransferProtocol": q.TransferProtocol, - "ImageURI": q.FirmwarePath, + "ImageURI": q.FirmwarePath, } data, err := json.Marshal(b) if err != nil { @@ -184,12 +183,10 @@ func GetUpdateStatus(q *UpdateParams) error { // return fmt.Errorf("could not read file: %v", err) // } - - // switch q.TransferProtocol { // case "HTTP": // default: // return fmt.Errorf("transfer protocol not supported") // } // return nil -// } \ No newline at end of file +// } diff --git a/main.go b/main.go index 6f5195a..ebe2d95 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/OpenChami/magellan/cmd" + "github.com/OpenCHAMI/magellan/cmd" ) func main() { From 8a7c0b3085c61e791615279365b55954378c7dad Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Fri, 3 Nov 2023 09:53:27 -0400 Subject: [PATCH 19/26] container registry entries must be lowercase --- .goreleaser.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8a3dd5d..427410a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -10,10 +10,10 @@ builds: - amd64 dockers: - image_templates: - - ghcr.io/OpenCHAMI/{{.ProjectName}}:latest - - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Tag }} - - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Major }} - - ghcr.io/OpenCHAMI/{{.ProjectName}}:{{ .Major }}.{{ .Minor }} + - ghcr.io/openchami/{{.ProjectName}}:latest + - ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }} + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }} + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }} build_flag_templates: - "--pull" - "--label=org.opencontainers.image.created={{.Date}}" From 5794a425934278c062ad4563926fe5699cf7f0dd Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Fri, 3 Nov 2023 15:18:22 +0000 Subject: [PATCH 20/26] Adding dev container to standardize Linux build --- .devcontainer/.env | 4 ++++ .devcontainer/Dockerfile | 13 +++++++++++ .devcontainer/devcontainer.json | 28 +++++++++++++++++++++++ .devcontainer/docker-compose.yml | 38 ++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 .devcontainer/.env create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/.env b/.devcontainer/.env new file mode 100644 index 0000000..15181a6 --- /dev/null +++ b/.devcontainer/.env @@ -0,0 +1,4 @@ +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=postgres +POSTGRES_HOSTNAME=localhost diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..d557f51 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/devcontainers/go:1-1.20-bullseye + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment the next lines to use go get to install anything else you need +# USER vscode +# RUN go get -x +# USER root + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f767ed3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go-postgres +{ + "name": "Go & PostgreSQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "features": { + "ghcr.io/marcozac/devcontainer-features/goreleaser:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": {} + + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + // "customizations": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5432], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..071a939 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.8' + +volumes: + postgres-data: + +services: + app: + build: + context: . + dockerfile: Dockerfile + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + + volumes: + - ../..:/workspaces:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + db: + image: postgres:latest + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + + + # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) From 6b6b694f421672a669e286ae83391812121da192 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Fri, 15 Mar 2024 17:20:50 -0600 Subject: [PATCH 21/26] Added auth-related dependencies --- go.mod | 16 ++++++++--- go.sum | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 1b5ea7b..cc9810a 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,12 @@ require ( github.com/Cray-HPE/hms-xname v1.3.0 github.com/bmc-toolbox/bmclib/v2 v2.0.1-0.20230714152943-a1b87e2ff47f github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 + github.com/go-chi/chi/v5 v5.0.12 github.com/jacobweinstock/registrar v0.4.7 github.com/jmoiron/sqlx v1.3.5 + github.com/lestrrat-go/jwx v1.2.29 github.com/mattn/go-sqlite3 v1.14.6 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/stmcginnis/gofish v0.14.0 @@ -19,18 +22,25 @@ require ( github.com/VictorLowther/simplexml v0.0.0-20180716164440-0bff93621230 // indirect github.com/VictorLowther/soap v0.0.0-20150314151524-8e36fca84b22 // indirect github.com/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/go-logr/logr v1.2.4 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef // indirect github.com/kr/text v0.2.0 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.3 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 8d5d7f4..0c2f55c 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,17 @@ github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= @@ -40,10 +47,25 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx v1.2.29 h1:QT0utmUJ4/12rmsVQrJ3u55bycPkKqGYuGT4tyRhxSQ= +github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -62,17 +84,66 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stmcginnis/gofish v0.14.0 h1:geECNAiG33JDB2x2xDkerpOOuXFqxp5YP3EFE3vd5iM= github.com/stmcginnis/gofish v0.14.0/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20230127130021-4ca2cb1a16b7 h1:o7Ps2IYdzLRolS9/nadqeMSHpa9k8pu8u+VKBFUG7cQ= golang.org/x/exp v0.0.0-20230127130021-4ca2cb1a16b7/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 7760857ae5ff8f9869cf509cbd2998b750a3a684 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Fri, 15 Mar 2024 17:22:00 -0600 Subject: [PATCH 22/26] Added login flow to get access token --- cmd/login.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 3 +- internal/login.go | 41 +++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 cmd/login.go create mode 100644 internal/login.go diff --git a/cmd/login.go b/cmd/login.go new file mode 100644 index 0000000..47b5823 --- /dev/null +++ b/cmd/login.go @@ -0,0 +1,84 @@ +package cmd + +import ( + "errors" + "fmt" + "net/http" + "os" + + magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/lestrrat-go/jwx/jwt" + "github.com/spf13/cobra" +) + +var ( + loginUrl string + targetHost string + targetPort int + tokenPath string + forceLogin bool + noBrowser bool +) + +var loginCmd = &cobra.Command{ + Use: "login", + Short: "Log in with identity provider for access token", + Long: "", + Run: func(cmd *cobra.Command, args []string) { + // check if we have a valid JWT before starting login + if !forceLogin { + // try getting the access token from env var + testToken := []byte(os.Getenv("OCHAMI_ACCESS_TOKEN")) + if testToken == nil { + // try reading access token from a file + b, err := os.ReadFile(tokenPath) + if err != nil { + fmt.Printf("failed to read access token from file: %v\n", err) + return + } + testToken = b + } + // parse into jwt.Token to validate + token, err := jwt.Parse(testToken) + if err != nil { + fmt.Printf("failed to parse access token contents: %v\n", err) + return + } + // check if the token is invalid and we need a new one + err = jwt.Validate(token) + if err != nil { + fmt.Printf("failed to validate access token...fetching a new one") + } else { + fmt.Printf("found a valid token...skipping login (use the '-f/--force' flag to login anyway)") + return + } + } + + // start the login flow + var err error + accessToken, err = magellan.Login(loginUrl, targetHost, targetPort) + if errors.Is(err, http.ErrServerClosed) { + fmt.Printf("\n=========================================\nServer closed.\n=========================================\n\n") + } else if err != nil { + fmt.Printf("failed to start server: %v\n", err) + } + + // if we got a new token successfully, save it to the token path + if accessToken != "" && tokenPath != "" { + err := os.WriteFile(tokenPath, []byte(accessToken), os.ModePerm) + if err != nil { + fmt.Printf("failed to write access token to file: %v\n", err) + } + } + }, +} + +func init() { + loginCmd.Flags().StringVar(&loginUrl, "url", "http://127.0.0.1:3333/login", "set the login URL") + loginCmd.Flags().StringVar(&targetHost, "target-host", "127.0.0.1", "set the target host to return the access code") + loginCmd.Flags().IntVar(&targetPort, "target-port", 5000, "set the target host to return the access code") + loginCmd.Flags().BoolVarP(&forceLogin, "force", "f", false, "start the login process even with a valid token") + loginCmd.Flags().StringVar(&tokenPath, "token-path", ".ochami-token", "set the path the load/save the access token") + loginCmd.Flags().BoolVar(&noBrowser, "no-browser", false, "prevent the default browser from being opened automatically") + rootCmd.AddCommand(loginCmd) +} diff --git a/cmd/root.go b/cmd/root.go index 82d6b4e..ca4c25f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,6 +8,7 @@ import ( ) var ( + accessToken string timeout int threads int ports []int @@ -21,7 +22,7 @@ var ( drivers []string preferredDriver string ipmitoolPath string - outputPath string + outputPath string verbose bool ) diff --git a/internal/login.go b/internal/login.go new file mode 100644 index 0000000..508a987 --- /dev/null +++ b/internal/login.go @@ -0,0 +1,41 @@ +package magellan + +import ( + "fmt" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/pkg/browser" +) + +func Login(loginUrl string, targetHost string, targetPort int) (string, error) { + var accessToken string + + // check and make sure the login URL isn't empty + if loginUrl == "" { + return "", fmt.Errorf("no login URL provided") + } + + // if a target host and port are provided, then add to URL + if targetHost != "" && targetPort > 0 && targetPort < 65536 { + loginUrl += fmt.Sprintf("?target=http://%s:%d", targetHost, targetPort) + } + + // open browser with the specified URL + err := browser.OpenURL(loginUrl) + if err != nil { + return "", fmt.Errorf("failed to open browser: %v", err) + } + + // start a temporary server to listen for token + s := http.Server{ + Addr: fmt.Sprintf("%s:%d", targetHost, targetPort), + } + r := chi.NewRouter() + r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // try and extract access token from headers + accessToken = r.Header.Get("access_token") + s.Close() + }) + return accessToken, s.ListenAndServe() +} From 09c5af44b903db0f83cdc9b0ae9df1e1afb0fff8 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Fri, 15 Mar 2024 17:43:25 -0600 Subject: [PATCH 23/26] Added checks for access token when running command --- cmd/collect.go | 10 ++++++++++ cmd/login.go | 20 ++++++++++---------- cmd/root.go | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/cmd/collect.go b/cmd/collect.go index e38cfe0..cb7f230 100644 --- a/cmd/collect.go +++ b/cmd/collect.go @@ -27,6 +27,16 @@ var collectCmd = &cobra.Command{ l.Log.Errorf("could not get states: %v", err) } + // try to load access token either from env var, file, or config if var not set + if accessToken == "" { + var err error + accessToken, err = LoadAccessToken() + if err != nil { + l.Log.Errorf("failed to load access token: %v", err) + } + } + + // if threads <= 0 { threads = mathutil.Clamp(len(probeStates), 1, 255) } diff --git a/cmd/login.go b/cmd/login.go index 47b5823..bd23e29 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -7,7 +7,9 @@ import ( "os" magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/OpenCHAMI/magellan/internal/log" "github.com/lestrrat-go/jwx/jwt" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -25,21 +27,19 @@ var loginCmd = &cobra.Command{ Short: "Log in with identity provider for access token", Long: "", Run: func(cmd *cobra.Command, args []string) { + // make application logger + l := log.NewLogger(logrus.New(), logrus.DebugLevel) + // check if we have a valid JWT before starting login if !forceLogin { // try getting the access token from env var - testToken := []byte(os.Getenv("OCHAMI_ACCESS_TOKEN")) - if testToken == nil { - // try reading access token from a file - b, err := os.ReadFile(tokenPath) - if err != nil { - fmt.Printf("failed to read access token from file: %v\n", err) - return - } - testToken = b + testToken, err := LoadAccessToken() + if err != nil { + l.Log.Errorf("failed to load access token: %v", err) } + // parse into jwt.Token to validate - token, err := jwt.Parse(testToken) + token, err := jwt.Parse([]byte(testToken)) if err != nil { fmt.Printf("failed to parse access token contents: %v\n", err) return diff --git a/cmd/root.go b/cmd/root.go index ca4c25f..e0e31de 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -50,9 +50,27 @@ func Execute() { } } +func LoadAccessToken() (string, error) { + // try to load token from env var + testToken := os.Getenv("OCHAMI_ACCESS_TOKEN") + if testToken != "" { + return testToken, nil + } + + // try reading access token from a file + b, err := os.ReadFile(tokenPath) + if err == nil { + return string(b), nil + } + + // TODO: try to load token from config + return "", fmt.Errorf("could not load from environment variable or file") +} + func init() { rootCmd.PersistentFlags().IntVar(&threads, "threads", -1, "set the number of threads") rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 30, "set the timeout") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", true, "set verbose flag") + rootCmd.PersistentFlags().StringVar(&accessToken, "access-token", "", "set the access token") rootCmd.PersistentFlags().StringVar(&dbpath, "db.path", "/tmp/magellan/magellan.db", "set the probe storage path") } From be1d473950195da5548f46801967e08fdc6d8a29 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Fri, 15 Mar 2024 17:43:53 -0600 Subject: [PATCH 24/26] Added access token to collect header if supplied --- internal/collect.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/collect.go b/internal/collect.go index b00ee63..5686066 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -50,6 +50,7 @@ type QueryParams struct { IpmitoolPath string OutputPath string ForceUpdate bool + AccessToken string } func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) { @@ -209,6 +210,11 @@ func CollectAll(probeStates *[]ScannedResult, l *log.Logger, q *QueryParams) err headers := make(map[string]string) headers["Content-Type"] = "application/json" + // use access token in authorization header if we have it + if q.AccessToken != "" { + headers["Authorization"] = "Bearer " + q.AccessToken + } + body, err := json.MarshalIndent(data, "", " ") if err != nil { l.Log.Errorf("could not marshal JSON: %v", err) From 8817e071b8973c6b94ac3cadaf0b4b61ff6ef877 Mon Sep 17 00:00:00 2001 From: Devon Bautista Date: Wed, 24 Apr 2024 09:57:48 -0600 Subject: [PATCH 25/26] Goreleaser: Add arm64 builds --- .goreleaser.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 427410a..946f525 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -8,6 +8,7 @@ builds: - linux goarch: - amd64 + - arm64 dockers: - image_templates: - ghcr.io/openchami/{{.ProjectName}}:latest From b5c0d9ce2af65c4d09b168b8c82087aec3af9c60 Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Mon, 6 May 2024 14:01:29 -0600 Subject: [PATCH 26/26] Changed cert flags and added cert for gofish --- cmd/collect.go | 25 +++++++-------- cmd/root.go | 3 +- cmd/update.go | 18 +++++------ internal/collect.go | 77 +++++++++++++++++++++++++++++++-------------- 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/cmd/collect.go b/cmd/collect.go index cb7f230..1855cbb 100644 --- a/cmd/collect.go +++ b/cmd/collect.go @@ -41,17 +41,17 @@ var collectCmd = &cobra.Command{ threads = mathutil.Clamp(len(probeStates), 1, 255) } q := &magellan.QueryParams{ - User: user, - Pass: pass, - Protocol: protocol, - Drivers: drivers, - Preferred: preferredDriver, - Timeout: timeout, - Threads: threads, - Verbose: verbose, - WithSecureTLS: withSecureTLS, - OutputPath: outputPath, - ForceUpdate: forceUpdate, + User: user, + Pass: pass, + Protocol: protocol, + Drivers: drivers, + Preferred: preferredDriver, + Timeout: timeout, + Threads: threads, + Verbose: verbose, + CaCertPath: cacertPath, + OutputPath: outputPath, + ForceUpdate: forceUpdate, } magellan.CollectAll(&probeStates, l, q) @@ -74,7 +74,6 @@ func init() { collectCmd.PersistentFlags().BoolVar(&forceUpdate, "force-update", false, "set flag to force update data sent to SMD ") collectCmd.PersistentFlags().StringVar(&preferredDriver, "preferred-driver", "ipmi", "set the preferred driver to use") collectCmd.PersistentFlags().StringVar(&ipmitoolPath, "ipmitool.path", "/usr/bin/ipmitool", "set the path for ipmitool") - collectCmd.PersistentFlags().BoolVar(&withSecureTLS, "secure-tls", false, "enable secure TLS") - collectCmd.PersistentFlags().StringVar(&certPoolFile, "cert-pool", "", "path to CA cert. (defaults to system CAs; used with --secure-tls=true)") + collectCmd.PersistentFlags().StringVar(&cacertPath, "ca-cert", "", "path to CA cert. (defaults to system CAs; used with --secure-tls=true)") rootCmd.AddCommand(collectCmd) } diff --git a/cmd/root.go b/cmd/root.go index e0e31de..dc5bb2d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,8 +14,7 @@ var ( ports []int hosts []string protocol string - withSecureTLS bool - certPoolFile string + cacertPath string user string pass string dbpath string diff --git a/cmd/update.go b/cmd/update.go index 94ccd71..f0ca82e 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -28,15 +28,14 @@ var updateCmd = &cobra.Command{ Component: component, TransferProtocol: transferProtocol, QueryParams: magellan.QueryParams{ - Drivers: []string{"redfish"}, - Preferred: "redfish", - Protocol: protocol, - Host: host, - User: user, - Pass: pass, - Timeout: timeout, - Port: port, - WithSecureTLS: withSecureTLS, + Drivers: []string{"redfish"}, + Preferred: "redfish", + Protocol: protocol, + Host: host, + User: user, + Pass: pass, + Timeout: timeout, + Port: port, }, } @@ -76,7 +75,6 @@ func init() { updateCmd.Flags().StringVar(&firmwareUrl, "firmware-url", "", "set the path to the firmware") updateCmd.Flags().StringVar(&firmwareVersion, "firmware-version", "", "set the version of firmware to be installed") updateCmd.Flags().StringVar(&component, "component", "", "set the component to upgrade") - updateCmd.Flags().BoolVar(&withSecureTLS, "secure-tls", false, "enable secure TLS") updateCmd.Flags().BoolVar(&status, "status", false, "get the status of the update") rootCmd.AddCommand(updateCmd) } diff --git a/internal/collect.go b/internal/collect.go index 5686066..4a2189a 100644 --- a/internal/collect.go +++ b/internal/collect.go @@ -35,25 +35,25 @@ const ( // NOTE: ...params were getting too long... type QueryParams struct { - Host string - Port int - Protocol string - User string - Pass string - Drivers []string - Threads int - Preferred string - Timeout int - WithSecureTLS bool - CertPoolFile string - Verbose bool - IpmitoolPath string - OutputPath string - ForceUpdate bool - AccessToken string + Host string + Port int + Protocol string + User string + Pass string + Drivers []string + Threads int + Preferred string + Timeout int + CaCertPath string + Verbose bool + IpmitoolPath string + OutputPath string + ForceUpdate bool + AccessToken string } func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) { + tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } @@ -75,9 +75,9 @@ func NewClient(l *log.Logger, q *QueryParams) (*bmclib.Client, error) { } // only work if valid cert is provided - if q.WithSecureTLS && q.CertPoolFile != "" { + if q.CaCertPath != "" { pool := x509.NewCertPool() - data, err := os.ReadFile(q.CertPoolFile) + data, err := os.ReadFile(q.CaCertPath) if err != nil { return nil, fmt.Errorf("could not read cert pool file: %v", err) } @@ -557,10 +557,12 @@ func CollectProcessors(q *QueryParams) ([]byte, error) { } func connectGofish(q *QueryParams) (*gofish.APIClient, error) { - config := makeGofishConfig(q) + config, err := makeGofishConfig(q) + if err != nil { + return nil, fmt.Errorf("failed to make gofish config: %v", err) + } c, err := gofish.Connect(config) if err != nil { - return nil, fmt.Errorf("could not connect to redfish endpoint: %v", err) } if c != nil { @@ -574,15 +576,42 @@ func connectGofish(q *QueryParams) (*gofish.APIClient, error) { return c, err } -func makeGofishConfig(q *QueryParams) gofish.ClientConfig { - url := baseRedfishUrl(q) +func makeGofishConfig(q *QueryParams) (gofish.ClientConfig, error) { + var ( + client = &http.Client{} + url = baseRedfishUrl(q) + config = gofish.ClientConfig{ + Endpoint: url, + Username: q.User, + Password: q.Pass, + Insecure: q.CaCertPath == "", + TLSHandshakeTimeout: q.Timeout, + HTTPClient: client, + // MaxConcurrentRequests: int64(q.Threads), // NOTE: this was added in latest gofish + } + ) + if q.CaCertPath != "" { + cacert, err := os.ReadFile(q.CaCertPath) + if err != nil { + return config, fmt.Errorf("failed to read CA cert file: %v", err) + } + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(cacert) + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: certPool, + }, + } + } return gofish.ClientConfig{ Endpoint: url, Username: q.User, Password: q.Pass, - Insecure: !q.WithSecureTLS, + Insecure: q.CaCertPath == "", TLSHandshakeTimeout: q.Timeout, - } + HTTPClient: client, + // MaxConcurrentRequests: int64(q.Threads), // NOTE: this was added in latest gofish + }, nil } func makeRequest[T any](client *bmclib.Client, fn func(context.Context) (T, error), timeout int) ([]byte, error) {