Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,25 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# Do NOT use `go-version-file: go.mod` here. go.mod pins a full patch
# version (`go 1.25.0`), so setup-go resolves 1.25.0 EXACTLY and the
# release binaries would ship the original 1.25.0 standard library — which
# has dozens of known vulnerabilities, including crypto/x509 name-
# constraint verification (GO-2025-4175), crypto/tls, encoding/pem
# (GO-2025-4009, on input koc genuinely parses: CA bundles, Vault-supplied
# certs, k8s secrets) and net/http HTTP/2 issues. A `1.25.x` range with
# check-latest resolves to the newest 1.25 patch instead, without raising
# the floor in go.mod (which would force a toolchain download and break the
# offline build for contributors).
# version, so setup-go would resolve it EXACTLY and the release binaries
# would ship that original .0 standard library instead of the newest patch
# — the trap that once shipped go1.25.0's crypto/x509 name-constraint
# verification (GO-2025-4175), crypto/tls and encoding/pem (GO-2025-4009,
# on input koc genuinely parses: CA bundles, Vault-supplied certs, k8s
# secrets) and net/http HTTP/2 bugs. An explicit range + check-latest
# resolves to the newest patch of that minor instead.
#
# This pin is deliberately a minor AHEAD of go.mod's floor. go.mod stays at
# `go 1.26.0` — the lowest the dependency graph allows (golang.org/x/crypto
# v0.56.0 forces it), so a contributor's `GOTOOLCHAIN=local` build stays
# offline on 1.26. The runner uses 1.27 because gofmt changed there: the
# pinned golangci-lint bundles Go 1.27's, and the tree is formatted with it
# (see the lint job). Move the two together or `make fmt` and the lint job
# will disagree about the same files, in both directions.
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand Down Expand Up @@ -112,7 +118,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand All @@ -137,7 +143,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand All @@ -155,18 +161,18 @@ jobs:
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

# Pin the exact golangci-lint version used in development and verify the
# download against the release's published SHA-256 checksums before
# extracting, so a corrupted or swapped asset fails the build.
- name: Install golangci-lint v2.12.2 (checksum-verified)
- name: Install golangci-lint v2.13.2 (checksum-verified)
env:
GOPROXY: "" # allow the tool download (build/test stay offline)
run: |
VER=2.12.2
VER=2.13.2
TARBALL="golangci-lint-${VER}-linux-amd64.tar.gz"
BASE="https://github.com/golangci/golangci-lint/releases/download/v${VER}"
curl -sSfL "${BASE}/${TARBALL}" -o "${TARBALL}"
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,18 @@ jobs:
with:
fetch-depth: 0 # full history + tags for git describe / release notes

# Explicit 1.25.x + check-latest instead of `go-version-file: go.mod`:
# go.mod pins the full patch `go 1.25.0`, so setup-go would resolve exactly
# 1.25.0 and every released binary would carry the original 1.25.0 standard
# library and its known vulnerabilities (crypto/x509 name-constraint
# verification GO-2025-4175, crypto/tls, encoding/pem GO-2025-4009,
# net/http HTTP/2). The floor stays at 1.25.0 in go.mod so contributors are
# not forced into a toolchain download.
# Explicit 1.27.x + check-latest instead of `go-version-file: go.mod`:
# go.mod pins a full patch, so setup-go would resolve it exactly and every
# released binary would carry that original .0 standard library rather than
# the newest patch's — the trap that once shipped go1.25.0's crypto/x509
# name-constraint verification (GO-2025-4175), crypto/tls, encoding/pem
# (GO-2025-4009) and net/http HTTP/2 bugs. The floor in go.mod stays lower
# (1.26.0, what golang.org/x/crypto v0.56.0 forces) so contributors need
# the smallest possible toolchain; keep this pin in step with ci.yml's.
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/supply-chain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.25.x"
go-version: "1.27.x"
check-latest: true
cache: false

Expand Down
27 changes: 23 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ the upstream `openstack` client's `noun → verb → flags` syntax and ships as
dependency-free binary for air-gapped / FSTEC-regulated deployment.

- **Module**: `github.com/ftarasenko/go-openstackclient` (binary name: `koc`)
- **Go**: see `go.mod` (currently `go 1.25`); target ≥ 1.22
- **Go**: `go.mod` floors the build at `go 1.26.0` (what `golang.org/x/crypto`
forces); **develop on 1.27+** — CI builds and formats there, and gofmt differs
(see "Build / test / lint")
- **SDK**: gophercloud **v2** (`github.com/gophercloud/gophercloud/v2`) — never v1 or the dead rackspace fork
- **CLI**: cobra + pflag; `golang.org/x/term` for terminal-width detection (rich gauges)
- **Deps are vendored** (`vendor/` is committed) — builds must reproduce offline
Expand Down Expand Up @@ -88,6 +90,17 @@ issues**, `go test ./...` green, the offline static build succeeds, and — if t
commit changes the command surface — `docs/coverage.md` is updated (see "Coverage
tracking").

**`gofmt` here means Go 1.27's.** 1.27 changed how a multi-value `return` whose
operands are composite literals is indented, and the two versions disagree in
both directions — 1.26's gofmt reformats what 1.27's wrote and vice versa. The
pinned golangci-lint bundles 1.27's (the whole v2.13 line is built with
go1.27.0) and enforces it as the `gofmt` formatter, so running `make fmt` under
an older toolchain silently reverts the tree and the lint job then rejects the
files it just touched. `go.mod`'s floor stays at 1.26.0 because nothing in the
code needs 1.27 — building and testing on 1.26 is fine, formatting is not. If
the golangci-lint pin ever moves to a build made with a newer Go, re-run
`make fmt` with that Go in the same commit.

## SonarQube (out-of-band static analysis)

SonarQube analysis is **not** part of the per-commit gate above and not part of
Expand Down Expand Up @@ -409,9 +422,15 @@ weakened by a check that happens to need a proxy.
separate job because `-race` needs `CGO_ENABLED=1` and that must never leak into
the shipped static binaries; and `lint`, with golangci-lint pinned and its
download checksum-verified against the release's published `checksums.txt`.
Go is resolved as `1.25.x` + `check-latest` rather than `go-version-file:
go.mod`, so the binaries get the newest 1.25 patch stdlib instead of the exact
version go.mod pins — see the comment in the workflow before changing it.
Go is resolved as `1.27.x` + `check-latest` rather than `go-version-file:
go.mod`, so the binaries get the newest 1.27 patch stdlib instead of the exact
version go.mod pins — see the comment in the workflow before changing it. The
pin sits a minor above go.mod's floor on purpose: it has to match the gofmt
the pinned golangci-lint bundles (above). Two rules follow. When a dependency
raises the floor in go.mod, these pins move in the same commit if the floor
would overtake them — every offline job runs `GOTOOLCHAIN=local` and fails
outright otherwise. And when the golangci-lint pin moves to a build made with
a newer Go, these pins and `make fmt` move with it.
- `.github/workflows/supply-chain.yml` — the **network-allowed** checks, on the
same triggers plus a weekly cron and `workflow_dispatch`. Three jobs:
`vendor-integrity` re-derives the vendor tree (`go mod download && go mod
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ lint:
golangci-lint run ./...

## fmt: format sources
# Needs gofmt from Go 1.27+. Go 1.27 changed the indentation of a multi-value
# return whose operands are composite literals, and the pinned golangci-lint
# bundles that gofmt, so an older one silently reverts the tree and the lint job
# then fails on files `make fmt` just "fixed".
fmt:
gofmt -w $(shell find . -name '*.go' -not -path './vendor/*')

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module github.com/ftarasenko/go-openstackclient

go 1.25.0
go 1.26.0

require (
github.com/gophercloud/gophercloud/v2 v2.14.0
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
golang.org/x/crypto v0.55.0
golang.org/x/crypto v0.56.0
golang.org/x/term v0.45.0
gopkg.in/yaml.v3 v3.0.1
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
Expand Down
18 changes: 9 additions & 9 deletions internal/auth/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,15 @@ func envOpenrc(t *testing.T) {
// cloudsYAMLResult is what clouds.Parse would hand back for the named cloud.
func cloudsYAMLResult() (gophercloud.AuthOptions, gophercloud.EndpointOpts) {
return gophercloud.AuthOptions{
IdentityEndpoint: "https://named-cloud.example:5000",
Username: "cloudsuser",
Password: "cloudssecret",
TenantName: "cloudsproject",
DomainName: "CloudsDom",
}, gophercloud.EndpointOpts{
Region: "cloudsregion",
Availability: gophercloud.AvailabilityPublic,
}
IdentityEndpoint: "https://named-cloud.example:5000",
Username: "cloudsuser",
Password: "cloudssecret",
TenantName: "cloudsproject",
DomainName: "CloudsDom",
}, gophercloud.EndpointOpts{
Region: "cloudsregion",
Availability: gophercloud.AvailabilityPublic,
}
}

// A named cloud must be used wholesale. Every auth flag defaults to its OS_*
Expand Down
12 changes: 6 additions & 6 deletions internal/cli/baremetal/allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ func extractAllocations(page pagination.Page) ([]allocation, error) {

func allocationShowFields(al *allocation) ([]string, []any) {
return []string{
"uuid", "name", "state", "node_uuid", "resource_class", "traits",
"candidate_nodes", "last_error", "extra", "owner", "created_at", "updated_at",
}, []any{
al.UUID, al.Name, al.State, al.NodeUUID, al.ResourceClass, al.Traits,
al.CandidateNodes, al.LastError, al.Extra, al.Owner, al.CreatedAt, al.UpdatedAt,
}
"uuid", "name", "state", "node_uuid", "resource_class", "traits",
"candidate_nodes", "last_error", "extra", "owner", "created_at", "updated_at",
}, []any{
al.UUID, al.Name, al.State, al.NodeUUID, al.ResourceClass, al.Traits,
al.CandidateNodes, al.LastError, al.Extra, al.Owner, al.CreatedAt, al.UpdatedAt,
}
}

// --- list -------------------------------------------------------------------
Expand Down
12 changes: 6 additions & 6 deletions internal/cli/network/qos.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ func newQoSPolicyCommand(a *auth.Options, o *output.Options) *cobra.Command {

func qosPolicyShowFields(p *policies.Policy) ([]string, []any) {
return []string{
"id", "name", "description", "shared", "is_default", "project_id",
"revision_number", "tags", "created_at", "updated_at",
}, []any{
p.ID, p.Name, p.Description, p.Shared, p.IsDefault, p.ProjectID,
p.RevisionNumber, strings.Join(p.Tags, ", "), p.CreatedAt, p.UpdatedAt,
}
"id", "name", "description", "shared", "is_default", "project_id",
"revision_number", "tags", "created_at", "updated_at",
}, []any{
p.ID, p.Name, p.Description, p.Shared, p.IsDefault, p.ProjectID,
p.RevisionNumber, strings.Join(p.Tags, ", "), p.CreatedAt, p.UpdatedAt,
}
}

func newQoSPolicyListCommand(a *auth.Options, o *output.Options) *cobra.Command {
Expand Down
4 changes: 4 additions & 0 deletions internal/cli/server/password_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func newTestKeypair(t *testing.T, plaintext string) (*rsa.PrivateKey, string) {
if err != nil {
t.Fatalf("generating a test key: %v", err)
}
// PKCS#1 v1.5 is deprecated as of Go 1.26, but it is the padding nova's
// get-password API stores and the one gophercloud's ExtractPassword
// undoes, so the fixture has to speak it.
//nolint:staticcheck // SA1019: nova's wire format, see above
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, &key.PublicKey, []byte(plaintext))
if err != nil {
t.Fatalf("encrypting the test password: %v", err)
Expand Down
38 changes: 17 additions & 21 deletions vendor/golang.org/x/crypto/ssh/certs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading