mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
5de598e85a
* ci: skip building unused E2E suite binaries in e2e.yml's single/multi jobs These two jobs immediately rebuild suite-aware binaries after Build Install Artifacts and never consume the tar's _bin contents, so the cross-compiled suite build was pure waste. build-install-artifacts.sh gains an optional --skip-e2e-build flag (positional contract unchanged); e2e-nightly.yml and the other callers keep building the full set. * refactor: replace hand-rolled IMDS client with AWS SDK v2 internal/imdscreds, cmd/ecs-agent's metadata fetch and its credentials provider used a hand-written IMDSv2 token/GET flow with manual RFC3339 expiry parsing. A malformed Expiration silently fell back to "never expires" instead of failing. Replace it with feature/ec2/imds, credentials/ec2rolecreds and aws.CredentialsCache, following the internal/gwsign/signer.go precedent. An unparseable Expiration is now a hard error from Fetch, covered by TestFetch_BadExpirationErrors. Output shapes to callers are unchanged, and IMDS base URLs ending in /latest keep working. * refactor: derive predastore bind config from spinifex.toml in spx itself predastore-start.sh awk-parsed spinifex.toml for the bind host/port and node_id, exported them as env vars, then exec'd spx. Move that derivation into predastoreStartCmd via derivePredastoreBind, which reads the raw (non-DIAL-normalized) nodes.<node>.predastore section, defaulting node_id to -1 (co-located) when the key is absent, same as the script. An explicit --host/--port/--node-id flag or SPINIFEX_PREDASTORE_* env var still overrides the derived value. Delete the now-unused script and point the systemd unit's ExecStart directly at spx service predastore start, matching the spinifex-viperblock.service precedent.
83 lines
2.9 KiB
Go
83 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func metadataStub(t *testing.T, enforceV2 bool) *httptest.Server {
|
|
t.Helper()
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPut {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
// Real IMDS echoes the requested TTL back as a response header; the SDK
|
|
// client parses it from there, not the body.
|
|
w.Header().Set("X-Aws-Ec2-Metadata-Token-Ttl-Seconds", "21600")
|
|
_, _ = w.Write([]byte("v2-token"))
|
|
})
|
|
guard := func(w http.ResponseWriter, r *http.Request) bool {
|
|
if enforceV2 && r.Header.Get("X-Aws-Ec2-Metadata-Token") != "v2-token" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
mux.HandleFunc("/latest/meta-data/instance-id", func(w http.ResponseWriter, r *http.Request) {
|
|
if guard(w, r) {
|
|
_, _ = w.Write([]byte("i-0abc123"))
|
|
}
|
|
})
|
|
mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, r *http.Request) {
|
|
if guard(w, r) {
|
|
_, _ = w.Write([]byte("us-east-1a"))
|
|
}
|
|
})
|
|
mux.HandleFunc("/latest/dynamic/instance-identity/document", func(w http.ResponseWriter, r *http.Request) {
|
|
if guard(w, r) {
|
|
_, _ = w.Write([]byte(`{"accountId":"123456789012","region":"us-east-1"}`))
|
|
}
|
|
})
|
|
srv := httptest.NewServer(mux)
|
|
t.Cleanup(srv.Close)
|
|
return srv
|
|
}
|
|
|
|
func TestFetchInstanceMetadata_OK(t *testing.T) {
|
|
srv := metadataStub(t, true)
|
|
got, err := fetchInstanceMetadata(srv.Client(), srv.URL+"/latest")
|
|
if err != nil {
|
|
t.Fatalf("fetch: %v", err)
|
|
}
|
|
if got.InstanceID != "i-0abc123" || got.AZ != "us-east-1a" || got.AccountID != "123456789012" {
|
|
t.Errorf("metadata mismatch: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestFetchInstanceMetadata_TokenRequired(t *testing.T) {
|
|
// Server returns 405 for non-PUT token; a GET-only client would 401 downstream.
|
|
srv := metadataStub(t, true)
|
|
if _, err := fetchInstanceMetadata(srv.Client(), srv.URL+"/latest"); err != nil {
|
|
t.Fatalf("v2 path should succeed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFetchInstanceMetadata_BadDocument(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/latest/api/token", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-Aws-Ec2-Metadata-Token-Ttl-Seconds", "21600")
|
|
_, _ = w.Write([]byte("t"))
|
|
})
|
|
mux.HandleFunc("/latest/meta-data/instance-id", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("i-1")) })
|
|
mux.HandleFunc("/latest/meta-data/placement/availability-zone", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("z")) })
|
|
mux.HandleFunc("/latest/dynamic/instance-identity/document", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("not json")) })
|
|
srv := httptest.NewServer(mux)
|
|
t.Cleanup(srv.Close)
|
|
if _, err := fetchInstanceMetadata(srv.Client(), srv.URL+"/latest"); err == nil {
|
|
t.Fatal("expected parse error on bad identity document")
|
|
}
|
|
}
|