Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2647c3331 | ||
|
|
d510e13461 | ||
|
|
470a34fdb1 | ||
|
|
a28cc7afce | ||
|
|
21dccc895d | ||
|
|
bb3c7576f8 | ||
|
|
02cbf7c014 | ||
|
|
a8c8df8604 | ||
|
|
5766a25144 | ||
|
|
1ccbb31b70 | ||
|
|
5e0dac6508 | ||
|
|
38d13eae73 | ||
|
|
b5e22790be | ||
|
|
beee7bd3f2 | ||
|
|
0c0e16914a | ||
|
|
9bd8f4962a | ||
|
|
4e42d4fb9e |
+3
-3
@@ -3,12 +3,12 @@ kind: pipeline
|
||||
name: coopcloud.tech/abra
|
||||
steps:
|
||||
- name: make check
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- make check
|
||||
|
||||
- name: xgettext-go
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
environment:
|
||||
GOPRIVATE: coopcloud.tech
|
||||
commands:
|
||||
@@ -43,7 +43,7 @@ steps:
|
||||
- tag
|
||||
|
||||
- name: make test
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
environment:
|
||||
ABRA_DIR: /root/.abra_test
|
||||
commands:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
vendor/** linguist-generated=true
|
||||
@@ -7,3 +7,4 @@
|
||||
/bin
|
||||
dist/
|
||||
tests/integration/.bats
|
||||
tests/resources/path_to_repo
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Build image
|
||||
FROM golang:1.27-alpine AS build
|
||||
FROM golang:1.26-alpine AS build
|
||||
|
||||
ENV GOPRIVATE=coopcloud.tech
|
||||
|
||||
|
||||
@@ -84,3 +84,53 @@ build-mo:
|
||||
|
||||
release:
|
||||
@goreleaser release --clean
|
||||
|
||||
CLIENT_VM := abra-client
|
||||
vm-client-status:
|
||||
sudo systemctl status microvm@$(CLIENT_VM).service
|
||||
vm-client-create:test-integration-hosts
|
||||
sudo microvm -f git+file://$$(pwd) -c $(CLIENT_VM)
|
||||
vm-client-start:
|
||||
sudo systemctl start microvm@$(CLIENT_VM).service
|
||||
vm-client-update:test-integration-hosts
|
||||
sudo microvm -R -f git+file://$$(pwd) -u $(CLIENT_VM)
|
||||
vm-client-run:
|
||||
sudo microvm -f git+file://$$(pwd) -r $(CLIENT_VM)
|
||||
vm-client-stop:
|
||||
sudo systemctl stop microvm@$(CLIENT_VM)
|
||||
vm-client-delete: vm-client-stop
|
||||
sudo rm -rf /var/lib/microvms/$(CLIENT_VM)
|
||||
vm-client-connect:
|
||||
ssh abra@10.0.0.2 -i ./tests/resources/local_integration_ssh
|
||||
|
||||
SERVER_VM := abra-server
|
||||
vm-server-status:
|
||||
sudo systemctl status microvm@$(SERVER_VM).service
|
||||
vm-server-create:
|
||||
sudo microvm -f git+file://$$(pwd) -c $(SERVER_VM)
|
||||
vm-server-start:
|
||||
sudo systemctl start microvm@$(SERVER_VM).service
|
||||
vm-server-update:
|
||||
sudo microvm -R -f git+file://$$(pwd) -u $(SERVER_VM)
|
||||
vm-server-run:
|
||||
sudo microvm -f git+file://$$(pwd) -r $(SERVER_VM)
|
||||
vm-server-stop:
|
||||
sudo systemctl stop microvm@$(SERVER_VM)
|
||||
vm-server-delete: vm-server-stop
|
||||
sudo rm -rf /var/lib/microvms/$(SERVER_VM)
|
||||
vm-server-connect:
|
||||
ssh abra@10.0.0.3 -i ./tests/resources/local_integration_ssh
|
||||
|
||||
test-integration-hosts:
|
||||
./scripts/tests/extra_hosts
|
||||
|
||||
# TEST_INTEGRATION_FILES overrides the default of running all test files
|
||||
ifdef TEST_INTEGRATION_FILES
|
||||
test_files := $(TEST_INTEGRATION_FILES);
|
||||
else
|
||||
test_files := *;
|
||||
endif
|
||||
test-integration: build
|
||||
chmod 600 ./tests/resources/local_integration_ssh
|
||||
ssh-add ./tests/resources/local_integration_ssh
|
||||
@bats -Tp --verbose-run tests/integration/$(test_files)
|
||||
|
||||
Generated
+38
@@ -18,6 +18,27 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"microvm": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"spectrum": "spectrum"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784666190,
|
||||
"narHash": "sha256-xgfS6slV7J3baMooNN1UuBi51RIgg9y0DbCxfSA0668=",
|
||||
"owner": "astro",
|
||||
"repo": "microvm.nix",
|
||||
"rev": "fa5340ac684cdce8a22b6d4a0bcebb0cc999275e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "astro",
|
||||
"repo": "microvm.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1778443072,
|
||||
@@ -37,9 +58,26 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"microvm": "microvm",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"spectrum": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1783694892,
|
||||
"narHash": "sha256-xO8f7Qng+18FK2UlB9vcrkxCaQMCt5WjCH24aW/11eg=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "24c4346e30fdea8d8e80f34aec3554a15a667d24",
|
||||
"revCount": 1410,
|
||||
"type": "git",
|
||||
"url": "https://spectrum-os.org/git/spectrum"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://spectrum-os.org/git/spectrum"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
|
||||
@@ -4,34 +4,103 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
microvm = {
|
||||
url = "github:astro/microvm.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
microvm,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
packages = rec {
|
||||
abra = pkgs.callPackage ./package.nix { };
|
||||
default = abra;
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
publicKey = builtins.readFile ./tests/resources/local_integration_ssh.pub;
|
||||
privateKey = builtins.readFile ./tests/resources/local_integration_ssh;
|
||||
pathToRepo = builtins.readFile ./tests/resources/path_to_repo;
|
||||
# local DNS aliases for the server host
|
||||
extraHosts = builtins.readFile ./tests/resources/extra_hosts;
|
||||
username = "abra";
|
||||
password = "abra";
|
||||
defaultDNS = [
|
||||
# Quad9.net
|
||||
"9.9.9.9"
|
||||
"149.112.112.112"
|
||||
"2620:fe::fe"
|
||||
"2620:fe::9"
|
||||
];
|
||||
in
|
||||
{
|
||||
nixosConfigurations = {
|
||||
abra-client = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
microvm.nixosModules.microvm
|
||||
./nix/hosts/client/configuration.nix
|
||||
];
|
||||
specialArgs = {
|
||||
inherit
|
||||
publicKey
|
||||
privateKey
|
||||
username
|
||||
password
|
||||
defaultDNS
|
||||
extraHosts
|
||||
pathToRepo
|
||||
;
|
||||
};
|
||||
};
|
||||
apps = rec {
|
||||
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
|
||||
default = abra;
|
||||
abra-server = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
microvm.nixosModules.microvm
|
||||
./nix/hosts/server/configuration.nix
|
||||
];
|
||||
specialArgs = {
|
||||
inherit
|
||||
publicKey
|
||||
privateKey
|
||||
username
|
||||
password
|
||||
defaultDNS
|
||||
;
|
||||
};
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
go_1_26
|
||||
gnumake
|
||||
};
|
||||
nixosModules = rec {
|
||||
host = {
|
||||
imports = [
|
||||
microvm.nixosModules.host
|
||||
./nix/modules/host.nix
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
default = host;
|
||||
};
|
||||
packages = rec {
|
||||
abra = pkgs.callPackage ./nix/package.nix { };
|
||||
default = abra;
|
||||
};
|
||||
apps = rec {
|
||||
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
|
||||
default = abra;
|
||||
};
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
# testing env variables
|
||||
BATS_LIB_PATH = "~/.local/share/bats/";
|
||||
TEST_SERVER = "abra.local";
|
||||
ABRA_DIR = "$HOME/.abra_test";
|
||||
|
||||
packages = with pkgs; [
|
||||
go_1_26
|
||||
gnumake
|
||||
gopls
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ require (
|
||||
github.com/docker/cli v28.4.0+incompatible
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/go-git/go-git/v5 v5.19.3
|
||||
github.com/go-git/go-git/v5 v5.19.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/leonelquinteros/gotext v1.7.2
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/schollz/progressbar/v3 v3.19.1
|
||||
golang.org/x/term v0.47.0
|
||||
golang.org/x/term v0.44.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
@@ -50,6 +50,7 @@ require (
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
@@ -59,7 +60,7 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.9.2 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.9.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -73,6 +74,7 @@ require (
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
@@ -94,7 +96,8 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.13 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.7.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
@@ -120,11 +123,11 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/crypto v0.56.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
@@ -149,10 +152,10 @@ require (
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sys v0.49.0
|
||||
golang.org/x/sys v0.46.0
|
||||
)
|
||||
|
||||
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
|
||||
|
||||
@@ -306,6 +306,7 @@ github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
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/decentral1se/cobra v1.10.2 h1:MZ8Ifi/jRels9sZrpSccDbUlK++3b2HlBODfv0Bh6x0=
|
||||
github.com/decentral1se/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
@@ -388,12 +389,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.9.2 h1:OXFSRyz4g20upsGDJgQG9Bak1l/ZEv8GHVYB52O71sE=
|
||||
github.com/go-git/go-billy/v5 v5.9.2/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
|
||||
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.19.3 h1:qHttbZ+Am7wEjdTpR1XMQ4rl42FK9SIXzZ4o9x7s6M4=
|
||||
github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW61gcOyhl4=
|
||||
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
|
||||
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -585,6 +586,8 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
|
||||
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -750,13 +753,14 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pjbgf/sha1cd v0.7.0 h1:ZRNPKHj+gfkLBf0KJv/p1Hmz+7bqCT5o311YX0nq+DA=
|
||||
github.com/pjbgf/sha1cd v0.7.0/go.mod h1:pKR5Li+qTCo+ebqITZfwPlJGoCFCvSO00qxjbNtTUFQ=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@@ -853,8 +857,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
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.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
@@ -942,9 +946,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -963,8 +966,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
|
||||
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1040,8 +1043,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1136,13 +1139,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
||||
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
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.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
|
||||
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1152,8 +1155,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Hosts
|
||||
These hosts are VMs used for running the integration tests on your local machine.
|
||||
The hosts contain a client where abra with the tests will be run and a server
|
||||
to simulate a remote machine.
|
||||
|
||||
## Prepare the host
|
||||
For this setup to work you need a machine with NixOS and flakes enabled.
|
||||
|
||||
1. Import the abra flake
|
||||
|
||||
The install example is based on the [using nix flakes wiki page](https://nixos.wiki/wiki/flakes#Using_nix_flakes_with_NixOS).
|
||||
```nix
|
||||
inputs = {
|
||||
abra = {
|
||||
url = "git+https://git.coopcloud.tech/toolshed/abra.git";
|
||||
};
|
||||
};
|
||||
```
|
||||
2. Add the host module to your configuration
|
||||
|
||||
Now add the host module to your configuration. At the toplevel of a flake it could look like this:
|
||||
```nix
|
||||
desktop = inputs.nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
abra.nixosModules.host
|
||||
./configuration.nix
|
||||
];
|
||||
specialArgs = inputs;
|
||||
};
|
||||
```
|
||||
3. Adjust the required config
|
||||
```nix
|
||||
abra.testing = {
|
||||
enable = true; # loads the required config, when set true
|
||||
externalInterface = "eth1"; # adjust this to your network interface that has access to the internet
|
||||
};
|
||||
```
|
||||
You can look up the interface with `ip link`. After that rebuild your system and you are ready to go.
|
||||
|
||||
## Get started
|
||||
All commands are run on the repository root path.
|
||||
1. Set path to repo
|
||||
|
||||
The VM will create a shared volume from the content of the file `tests/resources/path_to_repo`.
|
||||
Set the content of the file to the path of your abra repository and run the following command to prevent tracking of the file:
|
||||
```sh
|
||||
git update-index --assume-unchanged ./tests/resources/path_to_repo
|
||||
```
|
||||
This will share the repository from your machine with the VM and you can edit files while testing without rebuiling
|
||||
or restarting the VM.
|
||||
|
||||
2. Create the VMs
|
||||
```sh
|
||||
make vm-client-create
|
||||
make vm-server-create
|
||||
```
|
||||
3. Start the VMs
|
||||
```sh
|
||||
make vm-client-start
|
||||
make vm-server-start
|
||||
```
|
||||
After running that command you should be able to ping the machines.
|
||||
The client runs on 10.0.0.2 and server 10.0.0.3
|
||||
|
||||
4. Connect to the client
|
||||
|
||||
Running the following command will connect to the client VM via SSH.
|
||||
```sh
|
||||
make vm-client-connect
|
||||
```
|
||||
5. Run the tests
|
||||
```sh
|
||||
make test-integration
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
publicKey,
|
||||
username,
|
||||
pathToRepo,
|
||||
defaultDNS,
|
||||
extraHosts,
|
||||
...
|
||||
}:
|
||||
let
|
||||
index = 2;
|
||||
mac = "00:00:00:00:00:01";
|
||||
serverIp = "10.0.0.3";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/ssh.nix
|
||||
../../modules/user.nix
|
||||
../../modules/docker.nix
|
||||
];
|
||||
|
||||
microvm = {
|
||||
vcpu = 4;
|
||||
mem = 2049; # see issue related in microvm repo with 2048, so add 1MB
|
||||
interfaces = [
|
||||
{
|
||||
id = "vm${toString index}";
|
||||
type = "tap";
|
||||
inherit mac;
|
||||
}
|
||||
];
|
||||
shares = [
|
||||
{
|
||||
proto = "virtiofs";
|
||||
tag = "repo";
|
||||
# Source path can be absolute or relative
|
||||
# to /var/lib/microvms/$hostName
|
||||
source = "${lib.trim pathToRepo}";
|
||||
mountPoint = "/home/${username}/abra";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
boot.tmp = {
|
||||
useTmpfs = true;
|
||||
tmpfsSize = "2G";
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = "abra-client";
|
||||
useNetworkd = true;
|
||||
};
|
||||
systemd.network.networks."10-eth" = {
|
||||
matchConfig.MACAddress = mac;
|
||||
# Static IP configuration
|
||||
address = [
|
||||
"10.0.0.${toString index}/32"
|
||||
"fec0::${lib.toHexString index}/128"
|
||||
];
|
||||
routes = [
|
||||
{
|
||||
# A route to the host
|
||||
Destination = "10.0.0.0/32";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Route to server
|
||||
Destination = "${serverIp}/32";
|
||||
Gateway = "10.0.0.0";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Default route
|
||||
Destination = "0.0.0.0/0";
|
||||
Gateway = "10.0.0.0";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Default route
|
||||
Destination = "::/0";
|
||||
Gateway = "fec0::";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
];
|
||||
networkConfig = {
|
||||
# DNS servers no longer come from DHCP nor Router
|
||||
# Advertisements. Perhaps you want to change the defaults:
|
||||
DNS = defaultDNS;
|
||||
};
|
||||
};
|
||||
|
||||
# open the ports to allow ssh access from the host
|
||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||
networking.firewall.allowedUDPPorts = [ 22 ];
|
||||
|
||||
networking.extraHosts = extraHosts;
|
||||
|
||||
environment.variables = {
|
||||
CGO_ENABLED = 0;
|
||||
TEST_SERVER = "abra.local";
|
||||
ABRA_DIR = "$HOME/.abra_test";
|
||||
};
|
||||
|
||||
programs.ssh = {
|
||||
startAgent = true;
|
||||
knownHostsFiles = [
|
||||
(pkgs.writeText "local.keys" ''
|
||||
abra.local ${publicKey}
|
||||
'')
|
||||
];
|
||||
extraConfig = ''
|
||||
Host abra.local
|
||||
HostName abra.local
|
||||
Port 22
|
||||
User ${username}
|
||||
'';
|
||||
};
|
||||
programs.git = {
|
||||
enable = true;
|
||||
# many recipe commands need a preset git user
|
||||
config = [
|
||||
{
|
||||
user = {
|
||||
name = "abra dev";
|
||||
email = "helo@coopcloud.tech";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
# build dependencies, copied from flake.nix
|
||||
go_1_26
|
||||
gnumake
|
||||
# testing dependencies
|
||||
(bats.withLibraries (p: [
|
||||
p.bats-assert
|
||||
p.bats-file
|
||||
p.bats-support
|
||||
]))
|
||||
jq
|
||||
wget
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
serverIp,
|
||||
username,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Home Manager needs a bit of information about you and the
|
||||
# paths it should manage.
|
||||
home.username = username;
|
||||
home.homeDirectory = "/home/${username}";
|
||||
|
||||
# This value determines the Home Manager release that your
|
||||
# configuration is compatible with. This helps avoid breakage
|
||||
# when a new Home Manager release introduces backwards
|
||||
# incompatible changes.
|
||||
#
|
||||
# You can update Home Manager without changing this value. See
|
||||
# the Home Manager release notes for a list of state version
|
||||
# changes in each release.
|
||||
home.stateVersion = "26.05";
|
||||
|
||||
# Let Home Manager install and manage itself.
|
||||
programs.home-manager.enable = true;
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
enableDefaultConfig = false;
|
||||
settings = {
|
||||
"abra.local" = {
|
||||
HostName = serverIp;
|
||||
User = username;
|
||||
Port = 22;
|
||||
IdentityFile = "/home/${username}/abra/tests/resources/local_integration_ssh";
|
||||
};
|
||||
"*.abra.local" = {
|
||||
HostName = serverIp;
|
||||
User = username;
|
||||
Port = 22;
|
||||
IdentityFile = "/home/${username}/abra/tests/resources/local_integration_sshn";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
lib,
|
||||
defaultDNS,
|
||||
...
|
||||
}:
|
||||
let
|
||||
index = 3;
|
||||
mac = "00:00:00:00:00:02";
|
||||
clientIp = "10.0.0.2";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/ssh.nix
|
||||
../../modules/user.nix
|
||||
../../modules/docker.nix
|
||||
];
|
||||
|
||||
microvm = {
|
||||
vcpu = 4;
|
||||
mem = 8097; # see issue related in microvm repo with 2048, so add 1MB
|
||||
interfaces = [
|
||||
{
|
||||
id = "vm${toString index}";
|
||||
type = "tap";
|
||||
inherit mac;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
boot.tmp = {
|
||||
useTmpfs = true;
|
||||
tmpfsSize = "8G";
|
||||
};
|
||||
|
||||
networking = {
|
||||
hostName = "abra-server";
|
||||
useNetworkd = true;
|
||||
};
|
||||
systemd.network.networks."11-eth" = {
|
||||
matchConfig.MACAddress = mac;
|
||||
# Static IP configuration
|
||||
address = [
|
||||
"10.0.0.${toString index}/32"
|
||||
"fec0::${lib.toHexString index}/128"
|
||||
];
|
||||
routes = [
|
||||
{
|
||||
# A route to the host
|
||||
Destination = "10.0.0.0/32";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Route to server
|
||||
Destination = "${clientIp}/32";
|
||||
Gateway = "10.0.0.0";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Default route
|
||||
Destination = "0.0.0.0/0";
|
||||
Gateway = "10.0.0.0";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
{
|
||||
# Default route
|
||||
Destination = "::/0";
|
||||
Gateway = "fec0::";
|
||||
GatewayOnLink = true;
|
||||
}
|
||||
];
|
||||
networkConfig = {
|
||||
# DNS servers no longer come from DHCP nor Router
|
||||
# Advertisements. Perhaps you want to change the defaults:
|
||||
DNS = defaultDNS;
|
||||
};
|
||||
};
|
||||
|
||||
# open the ports to allow ssh access from the host
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
22
|
||||
80
|
||||
443
|
||||
1312 # deploy with udp and tcp on same port test
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
22
|
||||
80
|
||||
443
|
||||
1312 # deploy with udp and tcp on same port test
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{ ... }:
|
||||
{
|
||||
virtualisation.docker = {
|
||||
enable = true;
|
||||
liveRestore = false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
maxVMs = 3;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
abra.testing.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Enable the abra integration testing framework";
|
||||
};
|
||||
abra.testing.externalInterface = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = null;
|
||||
description = "Change this to the interface with upstream Internet access";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf config.abra.testing.enable {
|
||||
networking.useNetworkd = true;
|
||||
systemd.network.wait-online.enable = false;
|
||||
systemd.network.networks = builtins.listToAttrs (
|
||||
map (index: {
|
||||
name = "30-vm${toString index}";
|
||||
value = {
|
||||
matchConfig.Name = "vm${toString index}";
|
||||
# Host's addresses
|
||||
address = [
|
||||
"10.0.0.0/32"
|
||||
"fec0::/128"
|
||||
];
|
||||
# Setup routes to the VM
|
||||
routes = [
|
||||
{
|
||||
Destination = "10.0.0.${toString index}/32";
|
||||
}
|
||||
{
|
||||
Destination = "fec0::${lib.toHexString index}/128";
|
||||
}
|
||||
];
|
||||
# Enable routing
|
||||
networkConfig = {
|
||||
IPv4Forwarding = true;
|
||||
IPv6Forwarding = true;
|
||||
};
|
||||
};
|
||||
}) (lib.genList (i: i + 1) maxVMs)
|
||||
);
|
||||
networking.nat = {
|
||||
enable = true;
|
||||
internalIPs = [ "10.0.0.0/24" ];
|
||||
externalInterface = config.abra.testing.externalInterface;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
privateKey,
|
||||
publicKey,
|
||||
username,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# default host key location
|
||||
environment.etc."ssh/ssh_host_ed25519_key" = {
|
||||
text = privateKey;
|
||||
mode = "0600";
|
||||
};
|
||||
environment.etc."ssh/ssh_host_ed25519_key.pub" = {
|
||||
text = publicKey;
|
||||
mode = "0644";
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
AllowUsers = [ "${username}" ];
|
||||
};
|
||||
generateHostKeys = false;
|
||||
hostKeys = [
|
||||
{
|
||||
path = "/etc/ssh/ssh_host_ed25519_key";
|
||||
type = "ed25519";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
username,
|
||||
password,
|
||||
publicKey,
|
||||
...
|
||||
}:
|
||||
{
|
||||
users.users."${username}" = {
|
||||
isNormalUser = true;
|
||||
password = password;
|
||||
home = "/home/${username}";
|
||||
description = "Abra user";
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"networkmanager"
|
||||
"docker"
|
||||
];
|
||||
openssh.authorizedKeys.keys = [
|
||||
publicKey
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -52,6 +52,7 @@ func Clone(dir, url string) error {
|
||||
URL: url,
|
||||
Tags: git.AllTags,
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/main"),
|
||||
SingleBranch: true,
|
||||
})
|
||||
|
||||
if err != nil && gitCloneIgnoreErr(err) {
|
||||
@@ -70,6 +71,7 @@ func Clone(dir, url string) error {
|
||||
URL: url,
|
||||
Tags: git.AllTags,
|
||||
ReferenceName: plumbing.ReferenceName("refs/heads/master"),
|
||||
SingleBranch: true,
|
||||
})
|
||||
|
||||
if err != nil && gitCloneIgnoreErr(err) {
|
||||
|
||||
+37
-37
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-10-14 10:32-0400\n"
|
||||
"POT-Creation-Date: 2026-06-14 17:56+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -189,7 +189,7 @@ msgstr ""
|
||||
msgid "%d volumes removed successfully"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:198
|
||||
#: ./pkg/recipe/git.go:197
|
||||
#, c-format
|
||||
msgid "%s (%s) has locally unstaged changes?"
|
||||
msgstr ""
|
||||
@@ -419,7 +419,7 @@ msgstr ""
|
||||
msgid "%s sanitised as %s for new app"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:451
|
||||
#: ./pkg/recipe/git.go:445
|
||||
#, c-format
|
||||
msgid "%s service is missing image tag?"
|
||||
msgstr ""
|
||||
@@ -902,7 +902,7 @@ msgstr ""
|
||||
msgid "Generate the recipe catalogue"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:465
|
||||
#: ./pkg/recipe/git.go:459
|
||||
#, c-format
|
||||
msgid "GetRecipeVersions encountered error for %s: %s (collected %d versions)"
|
||||
msgstr ""
|
||||
@@ -1815,7 +1815,7 @@ msgstr ""
|
||||
msgid "are you sure?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:163
|
||||
#: ./pkg/recipe/git.go:162
|
||||
#, c-format
|
||||
msgid "attempting to checkout '%s' as chaos commit"
|
||||
msgstr ""
|
||||
@@ -1951,7 +1951,7 @@ msgstr ""
|
||||
msgid "cancelled"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/catalogue/catalogue.go:59 ./pkg/recipe/git.go:252
|
||||
#: ./pkg/catalogue/catalogue.go:59 ./pkg/recipe/git.go:251
|
||||
#, c-format
|
||||
msgid "cannot ensure %s is up-to-date, no git remotes configured"
|
||||
msgstr ""
|
||||
@@ -1966,7 +1966,7 @@ msgstr ""
|
||||
msgid "cannot get label %s for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:59
|
||||
#: ./pkg/recipe/git.go:58
|
||||
#, c-format
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
|
||||
msgstr ""
|
||||
@@ -2118,7 +2118,7 @@ msgstr ""
|
||||
msgid "cmd"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:476
|
||||
#: ./pkg/recipe/git.go:470
|
||||
#, c-format
|
||||
msgid "collected %s for %s"
|
||||
msgstr ""
|
||||
@@ -2456,7 +2456,7 @@ msgstr ""
|
||||
msgid "destination directory does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:379
|
||||
#: ./pkg/recipe/git.go:373
|
||||
#, c-format
|
||||
msgid "detected %s as tags for recipe %s"
|
||||
msgstr ""
|
||||
@@ -2653,7 +2653,7 @@ msgstr ""
|
||||
msgid "ensure recipe: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:57
|
||||
#: ./pkg/recipe/git.go:56
|
||||
#, c-format
|
||||
msgid "ensuring env version %s"
|
||||
msgstr ""
|
||||
@@ -2751,12 +2751,12 @@ msgstr ""
|
||||
msgid "failed to check git status of %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/branch.go:95 ./pkg/recipe/git.go:232
|
||||
#: ./pkg/git/branch.go:95 ./pkg/recipe/git.go:231
|
||||
#, c-format
|
||||
msgid "failed to check out %s in %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:418
|
||||
#: ./pkg/recipe/git.go:412
|
||||
#, c-format
|
||||
msgid "failed to check out %s in %s: %s"
|
||||
msgstr ""
|
||||
@@ -2806,7 +2806,7 @@ msgstr ""
|
||||
msgid "failed to generate random bytes: %w"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:427
|
||||
#: ./pkg/recipe/git.go:421
|
||||
#, c-format
|
||||
msgid "failed to get compose config for %s: %s"
|
||||
msgstr ""
|
||||
@@ -2844,7 +2844,7 @@ msgstr ""
|
||||
msgid "failed to parse image %s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:437
|
||||
#: ./pkg/recipe/git.go:431
|
||||
#, c-format
|
||||
msgid "failed to parse image for %s in %s: %s"
|
||||
msgstr ""
|
||||
@@ -2952,7 +2952,7 @@ msgstr ""
|
||||
msgid "fetch all recipes"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/catalogue/catalogue.go:84 ./pkg/recipe/git.go:290
|
||||
#: ./pkg/catalogue/catalogue.go:84 ./pkg/recipe/git.go:284
|
||||
#, c-format
|
||||
msgid "fetched latest git changes for %s"
|
||||
msgstr ""
|
||||
@@ -3070,12 +3070,12 @@ msgstr ""
|
||||
msgid "git changes pushed"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:423
|
||||
#: ./pkg/recipe/git.go:417
|
||||
#, c-format
|
||||
msgid "git checkout: %s in %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/clone.go:63 ./pkg/git/clone.go:100
|
||||
#: ./pkg/git/clone.go:64 ./pkg/git/clone.go:102
|
||||
#, c-format
|
||||
msgid "git clone %s: cancelled due to interrupt"
|
||||
msgstr ""
|
||||
@@ -3085,17 +3085,17 @@ msgstr ""
|
||||
msgid "git clone: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/clone.go:87
|
||||
#: ./pkg/git/clone.go:89
|
||||
#, c-format
|
||||
msgid "git clone: %s already exists"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/clone.go:58 ./pkg/git/clone.go:76 ./pkg/git/clone.go:85
|
||||
#: ./pkg/git/clone.go:59 ./pkg/git/clone.go:78 ./pkg/git/clone.go:87
|
||||
#, c-format
|
||||
msgid "git clone: %s cloned successfully"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/clone.go:67
|
||||
#: ./pkg/git/clone.go:68
|
||||
msgid "git clone: main branch failed, attempting master branch"
|
||||
msgstr ""
|
||||
|
||||
@@ -3150,7 +3150,7 @@ msgstr ""
|
||||
msgid "git.coopcloud.tech repo exists"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:390
|
||||
#: ./pkg/recipe/git.go:384
|
||||
#, c-format
|
||||
msgid "git: opening repository in %s"
|
||||
msgstr ""
|
||||
@@ -4061,7 +4061,7 @@ msgstr ""
|
||||
msgid "proceed?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:410
|
||||
#: ./pkg/recipe/git.go:404
|
||||
#, c-format
|
||||
msgid "processing %s for %s"
|
||||
msgstr ""
|
||||
@@ -4129,7 +4129,7 @@ msgstr ""
|
||||
msgid "re"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:158
|
||||
#: ./pkg/recipe/git.go:157
|
||||
#, c-format
|
||||
msgid "read %s as tags for recipe %s"
|
||||
msgstr ""
|
||||
@@ -4475,7 +4475,7 @@ msgstr ""
|
||||
msgid "retrieving docker auth token: failed create docker cli: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/recipe.go:656
|
||||
#: ./pkg/recipe/recipe.go:659
|
||||
msgid "retrieving recipes"
|
||||
msgstr ""
|
||||
|
||||
@@ -4865,17 +4865,17 @@ msgstr ""
|
||||
msgid "skipping secret (because it already exists) on %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:419
|
||||
#: ./pkg/recipe/git.go:413
|
||||
#, c-format
|
||||
msgid "skipping tag %s: checkout failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:428
|
||||
#: ./pkg/recipe/git.go:422
|
||||
#, c-format
|
||||
msgid "skipping tag %s: invalid compose config: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:438
|
||||
#: ./pkg/recipe/git.go:432
|
||||
#, c-format
|
||||
msgid "skipping tag %s: invalid image reference in service %s: %s"
|
||||
msgstr ""
|
||||
@@ -4978,7 +4978,7 @@ msgstr ""
|
||||
msgid "succeeded"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:185
|
||||
#: ./pkg/recipe/git.go:184
|
||||
#, c-format
|
||||
msgid "successfully checked %s out to %s in %s"
|
||||
msgstr ""
|
||||
@@ -5138,17 +5138,17 @@ msgstr ""
|
||||
msgid "un"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:194
|
||||
#: ./pkg/recipe/git.go:193
|
||||
#, c-format
|
||||
msgid "unable to check git clean status in %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:263
|
||||
#: ./pkg/recipe/git.go:262
|
||||
#, c-format
|
||||
msgid "unable to check out default branch in %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/git/clone.go:98
|
||||
#: ./pkg/git/clone.go:100
|
||||
#, c-format
|
||||
msgid "unable to clean up git clone of %s: %s"
|
||||
msgstr ""
|
||||
@@ -5222,7 +5222,7 @@ msgstr ""
|
||||
msgid "unable to discover SSH remote for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:274
|
||||
#: ./pkg/recipe/git.go:268
|
||||
#, c-format
|
||||
msgid "unable to fetch tags in %s: %s"
|
||||
msgstr ""
|
||||
@@ -5232,7 +5232,7 @@ msgstr ""
|
||||
msgid "unable to get container matching %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:286
|
||||
#: ./pkg/recipe/git.go:280
|
||||
#, c-format
|
||||
msgid "unable to git pull in %s: %s"
|
||||
msgstr ""
|
||||
@@ -5261,12 +5261,12 @@ msgstr ""
|
||||
msgid "unable to look up server context for %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:243
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:242
|
||||
#, c-format
|
||||
msgid "unable to open %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:258
|
||||
#: ./pkg/recipe/git.go:257
|
||||
#, c-format
|
||||
msgid "unable to open git work tree in %s: %s"
|
||||
msgstr ""
|
||||
@@ -5326,7 +5326,7 @@ msgstr ""
|
||||
msgid "unable to read new env %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:248
|
||||
#: ./pkg/recipe/git.go:247
|
||||
#, c-format
|
||||
msgid "unable to read remotes in %s: %s"
|
||||
msgstr ""
|
||||
@@ -5361,7 +5361,7 @@ msgstr ""
|
||||
msgid "unable to reset commit after failed release attempt: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:167
|
||||
#: ./pkg/recipe/git.go:166
|
||||
#, c-format
|
||||
msgid "unable to resolve '%s': %s"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+291
-625
File diff suppressed because it is too large
Load Diff
+1
-7
@@ -16,7 +16,6 @@ import (
|
||||
"coopcloud.tech/tagcmp"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/go-git/go-git/v5"
|
||||
gitCfg "github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
@@ -263,12 +262,7 @@ func (r Recipe) EnsureUpToDate() error {
|
||||
return errors.New(i18n.G("unable to check out default branch in %s: %s", r.Dir, err))
|
||||
}
|
||||
|
||||
// the refspec is passed explicitly, because a repository cloned by an older abra has a
|
||||
// single-branch refspec stored in its config and would otherwise never see other branches
|
||||
fetchOpts := &git.FetchOptions{
|
||||
Tags: git.AllTags,
|
||||
RefSpecs: []gitCfg.RefSpec{"+refs/heads/*:refs/remotes/origin/*"},
|
||||
}
|
||||
fetchOpts := &git.FetchOptions{Tags: git.AllTags}
|
||||
if err := repo.Fetch(fetchOpts); err != nil {
|
||||
if !strings.Contains(err.Error(), "already up-to-date") {
|
||||
return errors.New(i18n.G("unable to fetch tags in %s: %s", r.Dir, err))
|
||||
|
||||
+18
-15
@@ -592,7 +592,15 @@ func ReadReposMetadata(debug bool) (RepoCatalogue, error) {
|
||||
// sortRecipeVersions sorts the recipe semver versions
|
||||
func sortRecipeVersions(versions RecipeVersions) {
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return safeCompareVersions(getVersionString(versions[i]), getVersionString(versions[j]))
|
||||
version1, err := tagcmp.Parse(getVersionString(versions[i]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version2, err := tagcmp.Parse(getVersionString(versions[j]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return version1.IsLessThan(version2)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -608,23 +616,18 @@ func getVersionString(versionMap map[string]map[string]ServiceMeta) string {
|
||||
// sortVersionStrings sorts a list of semver version strings
|
||||
func sortVersionStrings(versions []string) {
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return safeCompareVersions(versions[i], versions[j])
|
||||
version1, err := tagcmp.Parse(versions[i])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version2, err := tagcmp.Parse(versions[j])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return version1.IsLessThan(version2)
|
||||
})
|
||||
}
|
||||
|
||||
// Invalid versions are sorted as less than valid ones.
|
||||
func safeCompareVersions(v1, v2 string) bool {
|
||||
version1, err1 := tagcmp.Parse(v1)
|
||||
if err1 != nil {
|
||||
return true
|
||||
}
|
||||
version2, err2 := tagcmp.Parse(v2)
|
||||
if err2 != nil {
|
||||
return false
|
||||
}
|
||||
return version1.IsLessThan(version2)
|
||||
}
|
||||
|
||||
// GetRecipeCatalogueVersions list the recipe versions listed in the recipe catalogue.
|
||||
func GetRecipeCatalogueVersions(recipeName string, catl RecipeCatalogue) ([]string, error) {
|
||||
var versions []string
|
||||
|
||||
@@ -123,41 +123,3 @@ func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
|
||||
assert.NotEqual(t, label, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a full test of comparison, as that occurs in the external
|
||||
// tagcmp package. This is a sunny-day integration test
|
||||
func TestSortRecipeVersionsSortsTwoValidStrings(t *testing.T) {
|
||||
versions := RecipeVersions{
|
||||
{"2.0.0": {}}, // Index 0
|
||||
{"1.0.0": {}}, // Index 1
|
||||
}
|
||||
|
||||
sortRecipeVersions(versions)
|
||||
|
||||
assert.Equal(t, "1.0.0", getVersionString(versions[0]), "Version 1.0.0 should be first")
|
||||
assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second")
|
||||
}
|
||||
|
||||
func TestSortRecipeVersionsSortsInvalidVersionStringToTheBottom(t *testing.T) {
|
||||
versions := RecipeVersions{
|
||||
{"2.0.0": {}}, // Index 0
|
||||
{"invalid": {}}, // Index 1
|
||||
}
|
||||
|
||||
sortRecipeVersions(versions)
|
||||
|
||||
assert.Equal(t, "invalid", getVersionString(versions[0]), "The invalid version should be first")
|
||||
assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second")
|
||||
}
|
||||
|
||||
func TestSortRecipeVersionsSortsInvalidVersionStringToTheBottomInReverse(t *testing.T) {
|
||||
versions := RecipeVersions{
|
||||
{"invalid": {}}, // Index 0
|
||||
{"2.0.0": {}}, // Index 1
|
||||
}
|
||||
|
||||
sortRecipeVersions(versions)
|
||||
|
||||
assert.Equal(t, "invalid", getVersionString(versions[0]), "The invalid version should be first")
|
||||
assert.Equal(t, "2.0.0", getVersionString(versions[1]), "Version 2.0.0 should be second")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.31.6
|
||||
image: nginx:1.31.2
|
||||
secrets:
|
||||
- test_pass_one
|
||||
- test_pass_two
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# This file generates local DNS aliases for the client VM for integration testing
|
||||
|
||||
echo "generating hosts file..."
|
||||
hosts_file_path="./tests/resources/extra_hosts"
|
||||
echo "removing old file..."
|
||||
rm -f $hosts_file_path
|
||||
|
||||
# the server domain
|
||||
server_ip="10.0.0.3"
|
||||
domain="abra.local"
|
||||
echo "$server_ip $domain" >> $hosts_file_path
|
||||
# static subdomains used in integration tests
|
||||
subdomains=("gitea" "zammad" "custom-html" "foo" "foobar")
|
||||
|
||||
for subdomain in "${subdomains[@]}"
|
||||
do
|
||||
echo "$server_ip $subdomain.$domain" >> $hosts_file_path
|
||||
done
|
||||
|
||||
search_dir=./tests/integration
|
||||
for entry in "$search_dir"/*.bats
|
||||
do
|
||||
file_name=$(basename "${entry}")
|
||||
# converting file names to testing subdomains
|
||||
subdomain=$(echo "$file_name" | tr . _)
|
||||
echo "$server_ip $subdomain.$domain" >> $hosts_file_path
|
||||
done
|
||||
|
||||
echo "writing extra hosts success"
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ignore changes to path_to_repo
|
||||
git update-index --assume-unchanged ./tests/resources/path_to_repo
|
||||
# write path to repo
|
||||
pwd > ./tests/resources/path_to_repo
|
||||
|
||||
# create or update a VM, start it and wait for successful ping
|
||||
start_vm () {
|
||||
if [ -d "/var/lib/microvms/abra-$1" ]; then
|
||||
make "vm-$1-update"
|
||||
else
|
||||
make "vm-$1-create"
|
||||
fi
|
||||
make "vm-$1-start"
|
||||
|
||||
host_available=0
|
||||
while [ $host_available == 0 ]; do
|
||||
if ping -c 1 "$2" &> /dev/null
|
||||
then
|
||||
host_available=1
|
||||
else
|
||||
echo "could not ping $1, waiting for 5 seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
start_vm "client" "10.0.0.2"
|
||||
start_vm "server" "10.0.0.3"
|
||||
|
||||
read -rp "Press enter to run all tests or specify test files: " TEST_INTEGRATION_FILES
|
||||
# -t option is needed for stdin output that is checked in integration tests
|
||||
ssh -t abra@10.0.0.2 -i ./tests/resources/local_integration_ssh "cd abra; make test-integration TEST_INTEGRATION_FILES=$TEST_INTEGRATION_FILES"
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -7,4 +7,5 @@
|
||||
|
||||
* Integration tests are in `./tests/integration`. Please see [these
|
||||
docs](https://docs.coopcloud.tech/abra/hack/#integration-tests) for
|
||||
instructions and tips on how to run them.
|
||||
instructions and tips on how to run them. If you want to run it locally via nix,
|
||||
read the [nix testing docs](../nix/hosts/README.md).
|
||||
|
||||
@@ -598,8 +598,7 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
|
||||
_inspect_env_test_app
|
||||
assert_success
|
||||
assert_output --partial "WITH_COMMENT=foo"
|
||||
|
||||
@@ -610,8 +609,7 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --force
|
||||
assert_success
|
||||
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
|
||||
_inspect_env_test_app
|
||||
assert_success
|
||||
refute_output --partial "WITH_COMMENT=foo"
|
||||
assert_output --partial "WITH_COMMENT=bar"
|
||||
@@ -629,8 +627,14 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
run docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' \
|
||||
if _is_local;
|
||||
then
|
||||
run docker service inspect --format "{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}" \
|
||||
"${TEST_APP_DOMAIN//./_}_app"
|
||||
else
|
||||
run ssh "$TEST_SERVER" "docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' ${TEST_APP_DOMAIN//./_}_app"
|
||||
fi
|
||||
|
||||
assert_success
|
||||
assert_output --partial "tcp=1312"
|
||||
assert_output --partial "udp=1312"
|
||||
|
||||
@@ -133,8 +133,7 @@ teardown(){
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input
|
||||
assert_success
|
||||
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
|
||||
_inspect_env_test_app
|
||||
assert_success
|
||||
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
|
||||
}
|
||||
|
||||
@@ -122,39 +122,6 @@ teardown(){
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "create new app with commit from another branch" {
|
||||
branchHash=$(_get_other_branch_hash)
|
||||
if [[ -z "$branchHash" ]]; then
|
||||
skip "$TEST_RECIPE has no branch besides main"
|
||||
fi
|
||||
|
||||
# re-clone the recipe the way an older abra did, with a single-branch refspec. --no-local
|
||||
# forces a real transfer, a local clone would hardlink the whole object database and leave
|
||||
# the commit reachable
|
||||
run rm -rf "$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||
assert_success
|
||||
|
||||
run git clone -q --no-local --single-branch --branch main \
|
||||
"$ABRA_DIR/origin-recipes/$TEST_RECIPE.git" "$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||
assert_success
|
||||
|
||||
# the commit has to be genuinely missing, otherwise this test passes for the wrong reason
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-parse --verify "$branchHash^{commit}"
|
||||
assert_failure
|
||||
|
||||
run $ABRA app new "$TEST_RECIPE" "$branchHash" \
|
||||
--no-input \
|
||||
--server "$TEST_SERVER" \
|
||||
--domain "$TEST_APP_DOMAIN"
|
||||
assert_success
|
||||
assert_exists "$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
|
||||
# the recipe names itself in TYPE, which differs per branch, so only the version is checked
|
||||
run grep -q "TYPE=.*:${branchHash}$" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "does not overwrite existing env files" {
|
||||
run $ABRA app new "$TEST_RECIPE" \
|
||||
--no-input \
|
||||
|
||||
@@ -140,15 +140,22 @@ teardown(){
|
||||
_undeploy_app
|
||||
|
||||
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
|
||||
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
|
||||
assert_success
|
||||
remote_ssh_command=""
|
||||
if _is_local;
|
||||
then
|
||||
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
|
||||
else
|
||||
remote_ssh_command="ssh '$TEST_SERVER'"
|
||||
run cat "$ABRA_DIR/recipes/abra-test-recipe/abra.sh" | ssh "$TEST_SERVER" docker config create "${sanitisedDomainName}_test_conf_v99 -"
|
||||
assert_success
|
||||
fi
|
||||
|
||||
assert bash -c "docker config ls | grep -q test_conf_v99"
|
||||
assert bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
|
||||
|
||||
run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
|
||||
assert_success
|
||||
|
||||
refute bash -c "docker config ls | grep -q test_conf_v99"
|
||||
refute bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
|
||||
}
|
||||
|
||||
@test "remove .env file" {
|
||||
|
||||
@@ -54,8 +54,7 @@ teardown(){
|
||||
--no-input
|
||||
assert_success
|
||||
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
|
||||
_inspect_env_test_app
|
||||
assert_success
|
||||
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ teardown(){
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input
|
||||
assert_success
|
||||
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
|
||||
_inspect_env_test_app
|
||||
assert_success
|
||||
assert_output --partial "$TEST_RECIIPE:0.2.0+1.21.0"
|
||||
assert_output --partial "$TEST_RECIPE:0.2.0+1.21.0"
|
||||
}
|
||||
|
||||
@@ -31,9 +31,18 @@ _ensure_ssh_agent() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
|
||||
if [ ! -S ~/.ssh/ssh_auth_sock ]; then
|
||||
if [[ ! -v SSH_AUTH_SOCK ]]; then
|
||||
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
|
||||
eval `ssh-agent`
|
||||
ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
|
||||
fi
|
||||
}
|
||||
|
||||
_is_local() {
|
||||
if [[ "$TEST_SERVER" == "default" ]];
|
||||
then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1,13 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_ensure_swarm() {
|
||||
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
|
||||
run docker swarm init --advertise-addr 127.0.0.1:2377
|
||||
assert_success
|
||||
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
|
||||
run docker swarm init --advertise-addr 127.0.0.1:2377
|
||||
assert_success
|
||||
fi
|
||||
|
||||
if ! $(docker network ls | grep -q 'proxy'); then
|
||||
run docker network create -d overlay proxy
|
||||
assert_success
|
||||
if ! docker network ls | grep -q 'proxy'; then
|
||||
run docker network create -d overlay proxy
|
||||
assert_success
|
||||
fi
|
||||
|
||||
if ! _is_local;
|
||||
then
|
||||
if [ "$(ssh "$TEST_SERVER" docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
|
||||
run ssh "$TEST_SERVER" docker swarm init --advertise-addr 127.0.0.1:2377
|
||||
assert_success
|
||||
fi
|
||||
|
||||
if ! ssh "$TEST_SERVER" docker network ls | grep -q 'proxy'; then
|
||||
run ssh "$TEST_SERVER" docker network create -d overlay proxy
|
||||
assert_success
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_inspect_env_test_app() {
|
||||
if _is_local;
|
||||
then
|
||||
containerId="$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)"
|
||||
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' "$containerId"
|
||||
else
|
||||
containerId="$(ssh "$TEST_SERVER" docker ps -f name="$TEST_APP_DOMAIN" -q)"
|
||||
run ssh "$TEST_SERVER" "docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' '$containerId'"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -56,13 +56,6 @@ _get_tag_hash() {
|
||||
echo $(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" rev-list -n 1 "$1")
|
||||
}
|
||||
|
||||
_get_other_branch_hash() {
|
||||
# asked from the origin mirror, not from the recipe checkout, so that the result does not
|
||||
# depend on what has been fetched. empty when the recipe only has a default branch
|
||||
echo $(git ls-remote "$ABRA_DIR/origin-recipes/$TEST_RECIPE.git" \
|
||||
| grep 'refs/heads/' | grep -v 'refs/heads/main$' | head -1 | cut -f1)
|
||||
}
|
||||
|
||||
_get_head_hash() {
|
||||
echo $(git -C "$ABRA_DIR/recipes/$TEST_RECIPE" show -s --format="%H" HEAD)
|
||||
}
|
||||
|
||||
@@ -38,8 +38,6 @@ teardown(){
|
||||
|
||||
run $ABRA recipe upgrade "custom-html" --no-input
|
||||
assert_success
|
||||
assert_output --partial 'can upgrade service: app'
|
||||
|
||||
assert_exists "$ABRA_DIR/recipes/custom-html"
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ teardown(){
|
||||
assert bash -c "docker context ls | grep -q $TEST_SERVER"
|
||||
|
||||
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
|
||||
assert_equal $server_dir_perms "700"
|
||||
assert_equal "$server_dir_perms" "700"
|
||||
}
|
||||
|
||||
@test "error if using name and --local together" {
|
||||
@@ -43,8 +43,8 @@ teardown(){
|
||||
assert bash -c "docker context ls | grep -q default"
|
||||
assert_output --partial 'local server successfully added'
|
||||
|
||||
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
|
||||
assert_equal $server_dir_perms "700"
|
||||
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/default")
|
||||
assert_equal "$server_dir_perms" "700"
|
||||
}
|
||||
|
||||
@test "create local server fails when no docker swarm" {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
10.0.0.3 abra.local
|
||||
10.0.0.3 gitea.abra.local
|
||||
10.0.0.3 zammad.abra.local
|
||||
10.0.0.3 custom-html.abra.local
|
||||
10.0.0.3 foo.abra.local
|
||||
10.0.0.3 foobar.abra.local
|
||||
10.0.0.3 app_check_bats.abra.local
|
||||
10.0.0.3 app_cmd_bats.abra.local
|
||||
10.0.0.3 app_config_bats.abra.local
|
||||
10.0.0.3 app_cp_bats.abra.local
|
||||
10.0.0.3 app_deploy_bats.abra.local
|
||||
10.0.0.3 app_deploy_env_version_bats.abra.local
|
||||
10.0.0.3 app_deploy_overview_bats.abra.local
|
||||
10.0.0.3 app_deploy_remote_recipes_bats.abra.local
|
||||
10.0.0.3 app_env_bats.abra.local
|
||||
10.0.0.3 app_env_version_bats.abra.local
|
||||
10.0.0.3 app_labels_bats.abra.local
|
||||
10.0.0.3 app_list_bats.abra.local
|
||||
10.0.0.3 app_logs_bats.abra.local
|
||||
10.0.0.3 app_move_bats.abra.local
|
||||
10.0.0.3 app_new_bats.abra.local
|
||||
10.0.0.3 app_ps_bats.abra.local
|
||||
10.0.0.3 app_remove_bats.abra.local
|
||||
10.0.0.3 app_restart_bats.abra.local
|
||||
10.0.0.3 app_rollback_bats.abra.local
|
||||
10.0.0.3 app_rollback_env_version_bats.abra.local
|
||||
10.0.0.3 app_rollback_overview_bats.abra.local
|
||||
10.0.0.3 app_run_bats.abra.local
|
||||
10.0.0.3 app_secret_bats.abra.local
|
||||
10.0.0.3 app_secret_env_version_bats.abra.local
|
||||
10.0.0.3 app_services_bats.abra.local
|
||||
10.0.0.3 app_undeploy_bats.abra.local
|
||||
10.0.0.3 app_undeploy_env_version_bats.abra.local
|
||||
10.0.0.3 app_undeploy_overview_bats.abra.local
|
||||
10.0.0.3 app_upgrade_bats.abra.local
|
||||
10.0.0.3 app_upgrade_env_version_bats.abra.local
|
||||
10.0.0.3 app_upgrade_overview_bats.abra.local
|
||||
10.0.0.3 app_volume_bats.abra.local
|
||||
10.0.0.3 autocomplete_bats.abra.local
|
||||
10.0.0.3 catalogue_bats.abra.local
|
||||
10.0.0.3 dirs_bats.abra.local
|
||||
10.0.0.3 install_bats.abra.local
|
||||
10.0.0.3 recipe_diff_bats.abra.local
|
||||
10.0.0.3 recipe_fetch_bats.abra.local
|
||||
10.0.0.3 recipe_lint_bats.abra.local
|
||||
10.0.0.3 recipe_list_bats.abra.local
|
||||
10.0.0.3 recipe_new_bats.abra.local
|
||||
10.0.0.3 recipe_release_bats.abra.local
|
||||
10.0.0.3 recipe_reset_bats.abra.local
|
||||
10.0.0.3 recipe_upgrade_bats.abra.local
|
||||
10.0.0.3 recipe_version_bats.abra.local
|
||||
10.0.0.3 server_add_bats.abra.local
|
||||
10.0.0.3 server_list_bats.abra.local
|
||||
10.0.0.3 server_prune_bats.abra.local
|
||||
10.0.0.3 server_remove_bats.abra.local
|
||||
10.0.0.3 upgrade_bats.abra.local
|
||||
10.0.0.3 version_bats.abra.local
|
||||
@@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBQ967UhxmFkq71WXvVRkEmtehCGsEnBVppAXxgtHHm7wAAAJhFGwTnRRsE
|
||||
5wAAAAtzc2gtZWQyNTUxOQAAACBQ967UhxmFkq71WXvVRkEmtehCGsEnBVppAXxgtHHm7w
|
||||
AAAEAnHEZcf2NeRQEcJC/aVgUWsdOz+vQgEG9ZY+3ErCeaKFD3rtSHGYWSrvVZe9VGQSa1
|
||||
6EIawScFWmkBfGC0cebvAAAAFWFicmEgaW50ZWdyYXRpb24gdGVzdA==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFD3rtSHGYWSrvVZe9VGQSa16EIawScFWmkBfGC0cebv abra integration test
|
||||
@@ -0,0 +1 @@
|
||||
/path/to/this/repo
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.31.6
|
||||
image: nginx:1.31.2
|
||||
networks:
|
||||
- proxy
|
||||
deploy:
|
||||
|
||||
+3
-3
@@ -3,16 +3,16 @@ kind: pipeline
|
||||
name: coopcloud.tech/tagcmp
|
||||
steps:
|
||||
- name: gofmt
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- test -z "$(gofmt -l .)"
|
||||
|
||||
- name: go build
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- go build -v .
|
||||
|
||||
- name: go test
|
||||
image: golang:1.27
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- go test . -cover
|
||||
|
||||
Generated
Vendored
-1
@@ -18,7 +18,6 @@
|
||||
// tag is deprecated and thus should not be used.
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
//go:build !js && !appengine && !safe && !disableunsafe && go1.4
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
Generated
Vendored
-1
@@ -16,7 +16,6 @@
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
//go:build js || appengine || safe || disableunsafe || !go1.4
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
Generated
Vendored
Generated
Vendored
+15
-15
@@ -254,15 +254,15 @@ pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
- Pointers are dereferenced and followed
|
||||
- Circular data structures are detected and handled properly
|
||||
- Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
- Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
- Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by modifying the public members
|
||||
of c. See ConfigState for options documentation.
|
||||
@@ -295,12 +295,12 @@ func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{})
|
||||
|
||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||
//
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
func NewDefaultConfig() *ConfigState {
|
||||
return &ConfigState{Indent: " "}
|
||||
}
|
||||
Generated
Vendored
+61
-67
@@ -21,36 +21,35 @@ debugging.
|
||||
A quick overview of the additional features spew provides over the built-in
|
||||
printing facilities for Go data types are as follows:
|
||||
|
||||
- Pointers are dereferenced and followed
|
||||
- Circular data structures are detected and handled properly
|
||||
- Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
- Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
- Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
|
||||
There are two different approaches spew allows for dumping Go data structures:
|
||||
|
||||
- Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
- A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
* Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
|
||||
# Quick Start
|
||||
Quick Start
|
||||
|
||||
This section demonstrates how to quickly get started with spew. See the
|
||||
sections below for further details on formatting and configuration options.
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
@@ -59,13 +58,12 @@ Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||
%#+v (adds types and pointer addresses):
|
||||
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
# Configuration Options
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
@@ -76,52 +74,51 @@ equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
- Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
- MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
- DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
- DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
- DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
- DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
- ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
|
||||
- SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
* SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
- SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
# Dump Usage
|
||||
Dump Usage
|
||||
|
||||
Simply call spew.Dump with a list of variables you want to dump:
|
||||
|
||||
@@ -136,7 +133,7 @@ A third option is to call spew.Sdump to get the formatted output as a string:
|
||||
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
# Sample Dump Output
|
||||
Sample Dump Output
|
||||
|
||||
See the Dump example for details on the setup of the types and variables being
|
||||
shown here.
|
||||
@@ -153,14 +150,13 @@ shown here.
|
||||
|
||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||
command as shown.
|
||||
|
||||
([]uint8) (len=32 cap=32) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
|
||||
# Custom Formatter
|
||||
Custom Formatter
|
||||
|
||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||
so that it integrates cleanly with standard fmt package printing functions. The
|
||||
@@ -174,7 +170,7 @@ standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
# Custom Formatter Usage
|
||||
Custom Formatter Usage
|
||||
|
||||
The simplest way to make use of the spew custom formatter is to call one of the
|
||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||
@@ -188,17 +184,15 @@ functions have syntax you are most likely already familiar with:
|
||||
|
||||
See the Index for the full list convenience functions.
|
||||
|
||||
# Sample Formatter Output
|
||||
Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
@@ -207,7 +201,7 @@ Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
See the Printf example for details on the setup of variables being shown
|
||||
here.
|
||||
|
||||
# Errors
|
||||
Errors
|
||||
|
||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||
detects them and handles them internally by printing the panic information
|
||||
Generated
Vendored
+9
-9
@@ -488,15 +488,15 @@ pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
- Pointers are dereferenced and followed
|
||||
- Circular data structures are detected and handled properly
|
||||
- Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
- Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
- Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by an exported package global,
|
||||
spew.Config. See ConfigState for options documentation.
|
||||
Generated
Vendored
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
ARG GO_VERSION=1.20.8
|
||||
ARG ALPINE_VERSION=3.18
|
||||
ARG XX_VERSION=1.10.0
|
||||
ARG XX_VERSION=1.2.1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
|
||||
-1
@@ -5,4 +5,3 @@ profile.out
|
||||
.tmp/
|
||||
.git-dist/
|
||||
.vscode
|
||||
build/tools/
|
||||
|
||||
-4
@@ -329,10 +329,6 @@ func (c *Config) unmarshalCore() {
|
||||
if parsed := parseConfigBool(s.Options.Get(protectHFSKey)); parsed.IsSet() {
|
||||
c.Core.ProtectHFS = parsed
|
||||
}
|
||||
|
||||
if s.Options.Get(repositoryFormatVersionKey) == string(format.Version_1) {
|
||||
c.Core.RepositoryFormatVersion = format.Version_1
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) unmarshalUser() {
|
||||
|
||||
+3
-25
@@ -139,12 +139,8 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
|
||||
m[otp.Hash()] = otp
|
||||
}
|
||||
|
||||
// visiting holds the objects on the current resolution path, so that a
|
||||
// delta chain looping back on itself can be detected and broken.
|
||||
visiting := make(map[plumbing.Hash]bool)
|
||||
|
||||
for _, otp := range objectsToPack {
|
||||
if err := dw.fixAndBreakChainsOne(m, otp, visiting); err != nil {
|
||||
if err := dw.fixAndBreakChainsOne(m, otp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -152,11 +148,7 @@ func (dw *deltaSelector) fixAndBreakChains(objectsToPack []*ObjectToPack) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dw *deltaSelector) fixAndBreakChainsOne(
|
||||
objectsToPack map[plumbing.Hash]*ObjectToPack,
|
||||
otp *ObjectToPack,
|
||||
visiting map[plumbing.Hash]bool,
|
||||
) error {
|
||||
func (dw *deltaSelector) fixAndBreakChainsOne(objectsToPack map[plumbing.Hash]*ObjectToPack, otp *ObjectToPack) error {
|
||||
if !otp.Object.Type().IsDelta() {
|
||||
return nil
|
||||
}
|
||||
@@ -182,21 +174,7 @@ func (dw *deltaSelector) fixAndBreakChainsOne(
|
||||
return dw.undeltify(otp)
|
||||
}
|
||||
|
||||
// Mark this object as being resolved before looking at its base, so that
|
||||
// a delta based on itself is caught by the check below.
|
||||
h := otp.Hash()
|
||||
visiting[h] = true
|
||||
defer delete(visiting, h)
|
||||
|
||||
// A delta chain that loops back onto an object we are already resolving
|
||||
// cannot be written: every delta needs its base written first. Break the
|
||||
// chain here instead of following the cycle, which would recurse until
|
||||
// the goroutine stack is exhausted.
|
||||
if visiting[do.BaseHash()] {
|
||||
return dw.undeltify(otp)
|
||||
}
|
||||
|
||||
if err := dw.fixAndBreakChainsOne(objectsToPack, base, visiting); err != nil {
|
||||
if err := dw.fixAndBreakChainsOne(objectsToPack, base); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+2
-34
@@ -474,14 +474,6 @@ type TreeWalker struct {
|
||||
recursive bool
|
||||
seen map[plumbing.Hash]bool
|
||||
|
||||
// skipPathValidation disables the pathutil.ValidTreePath check in Next.
|
||||
// It is set by inspection-only callers (e.g. the revlist object walk)
|
||||
// that never funnel entry names into the filesystem and must enumerate
|
||||
// trees faithfully, including entries with names upstream Git accepts
|
||||
// but that are unsafe to materialise (control characters, `.git`-shaped
|
||||
// names).
|
||||
skipPathValidation bool
|
||||
|
||||
s storer.EncodedObjectStorer
|
||||
t *Tree
|
||||
}
|
||||
@@ -504,32 +496,10 @@ func NewTreeWalker(t *Tree, recursive bool, seen map[plumbing.Hash]bool) *TreeWa
|
||||
}
|
||||
}
|
||||
|
||||
// SkipPathValidation disables the pathutil.ValidTreePath check performed by
|
||||
// Next, and must be called before the first Next.
|
||||
//
|
||||
// It is for inspection-only walks that never funnel an entry name into the
|
||||
// filesystem — enumerating which objects exist, rather than materialising
|
||||
// them — and that must therefore see the tree faithfully, including entries
|
||||
// whose names upstream Git accepts but that are unsafe to check out. Callers
|
||||
// that hand the returned name to filesystem or archive output must not use
|
||||
// it; path safety for those is enforced at the materialisation boundaries
|
||||
// (FindEntry, TreeEntryFile, archive, FileIter).
|
||||
func (w *TreeWalker) SkipPathValidation() {
|
||||
w.skipPathValidation = true
|
||||
}
|
||||
|
||||
// Next returns the next object from the tree. Objects are returned in order
|
||||
// and subtrees are included. After the last object has been returned further
|
||||
// calls to Next() will return io.EOF.
|
||||
//
|
||||
// Each entry's name is validated against pathutil.ValidTreePath as it
|
||||
// surfaces, so callers that funnel the returned name into filesystem
|
||||
// or archive output can trust it is free of `.git`-shaped components,
|
||||
// HFS+/NTFS variants, Windows reserved names, and traversal sequences.
|
||||
// A malformed entry stops the walk with the validator's error;
|
||||
// inspection-only callers that need to enumerate raw, unvalidated
|
||||
// names can read Tree.Entries directly or call SkipPathValidation.
|
||||
//
|
||||
// In the current implementation any objects which cannot be found in the
|
||||
// underlying repository will be skipped automatically. It is possible that this
|
||||
// may change in future versions.
|
||||
@@ -566,10 +536,8 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !w.skipPathValidation {
|
||||
if err := pathutil.ValidTreePath(entry.Name); err != nil {
|
||||
return name, entry, err
|
||||
}
|
||||
if err := pathutil.ValidTreePath(entry.Name); err != nil {
|
||||
return name, entry, err
|
||||
}
|
||||
|
||||
if entry.Mode == filemode.Dir {
|
||||
|
||||
-6
@@ -106,12 +106,6 @@ func transformChildren(t *Tree) ([]noder.Noder, error) {
|
||||
ret := make([]noder.Noder, 0, len(t.Entries))
|
||||
|
||||
walker := NewTreeWalker(t, false, nil) // don't recurse
|
||||
// The diff walk is read-only and never materialises entry names into the
|
||||
// filesystem, so it must enumerate the tree faithfully — including entries
|
||||
// with names that are unsafe to check out but valid per upstream Git (e.g.
|
||||
// control characters). Path safety is enforced at materialisation
|
||||
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
|
||||
walker.SkipPathValidation()
|
||||
// don't defer walker.Close() for efficiency reasons.
|
||||
for {
|
||||
_, e, err = walker.Next()
|
||||
|
||||
-42
@@ -110,48 +110,6 @@ func (r ReferenceName) IsTag() bool {
|
||||
return strings.HasPrefix(string(r), refTagPrefix)
|
||||
}
|
||||
|
||||
// IsSafe reports whether the reference name can be safely turned into a path
|
||||
// under the .git directory, mirroring Git's refname_is_safe (refs.c). A name
|
||||
// is safe when it is either:
|
||||
//
|
||||
// - under "refs/", non-empty after the prefix, containing no backslash and
|
||||
// no empty, "." or ".." path component (so it cannot escape the refs/
|
||||
// sub-tree, or alias another name, once turned into a path); or
|
||||
// - a one-level pseudo-ref whose spelling is restricted to [A-Z_]
|
||||
// (e.g. HEAD, ORIG_HEAD, FETCH_HEAD).
|
||||
//
|
||||
// Everything else — a lowercase or mixed one-level name such as "config" or
|
||||
// "index", an absolute or drive-prefixed name, or a refs/ name that escapes —
|
||||
// is unsafe, because it could resolve onto unrelated repository metadata.
|
||||
func (r ReferenceName) IsSafe() bool {
|
||||
s := string(r)
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if rest, ok := strings.CutPrefix(s, refPrefix); ok {
|
||||
// '\' is a path separator on Windows, so a refs/ name containing one
|
||||
// could escape the sub-tree or alias another name once turned into a
|
||||
// path; reject it outright (check_refname_format forbids '\' too).
|
||||
if rest == "" || strings.Contains(rest, "\\") {
|
||||
return false
|
||||
}
|
||||
for part := range strings.SplitSeq(rest, "/") {
|
||||
if part == "" || part == "." || part == ".." {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
if (s[i] < 'A' || s[i] > 'Z') && s[i] != '_' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r ReferenceName) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
-7
@@ -187,13 +187,6 @@ func iterateCommitTrees(
|
||||
cb(tree.Hash)
|
||||
|
||||
treeWalker := object.NewTreeWalker(tree, true, seen)
|
||||
// This walk only enumerates which objects are reachable, to decide what
|
||||
// to send; it never materialises an entry name into the filesystem. It
|
||||
// must therefore enumerate the tree faithfully, including entries with
|
||||
// names that are unsafe to check out but valid per upstream Git (e.g.
|
||||
// control characters). Path safety is enforced at materialisation
|
||||
// boundaries (FindEntry, TreeEntryFile, archive, FileIter), not here.
|
||||
treeWalker.SkipPathValidation()
|
||||
|
||||
for {
|
||||
_, e, err := treeWalker.Next()
|
||||
|
||||
+22
-379
@@ -6,12 +6,8 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -77,7 +73,7 @@ func advertisedReferences(ctx context.Context, s *session, serviceName string) (
|
||||
s.endpoint.String(), infoRefsPath, serviceName,
|
||||
)
|
||||
|
||||
req, err := newRequest(http.MethodGet, url, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -181,24 +177,6 @@ func NewClient(c *http.Client) transport.Transport {
|
||||
// and other custom options specific to the client.
|
||||
// If the net/http client is nil or empty, it will use a net/http client configured
|
||||
// with http.DefaultTransport.
|
||||
//
|
||||
// Credentials this client adds where the transport cannot see them are not
|
||||
// subject to the redirect stripping described on AuthMethod: a RoundTripper
|
||||
// injects after the hop is decided, and Client.Jar is consulted after
|
||||
// CheckRedirect, so a domain cookie still follows a redirect to a subdomain
|
||||
// the transport counts as another origin. Apply them in an AuthMethod instead
|
||||
// if that is not wanted. A CheckRedirect hook set on this client runs
|
||||
// alongside the transport's own, but any header it adds when a redirect
|
||||
// leaves the repository's origin is discarded the same way.
|
||||
//
|
||||
// A RoundTripper is therefore also how to authenticate to a new origin a
|
||||
// redirect has moved the repository to: match on the request URL and inject
|
||||
// the credential only for that origin, so it is not sent anywhere else. The
|
||||
// transport keeps its own CheckRedirect on the copy it makes of this client,
|
||||
// so the policy and the origin checks still apply.
|
||||
//
|
||||
// None of this applies to a RoundTripper that follows redirects itself:
|
||||
// CheckRedirect is not consulted then, so no stripping happens at all.
|
||||
func NewClientWithOptions(c *http.Client, opts *ClientOptions) transport.Transport {
|
||||
if c == nil {
|
||||
c = &http.Client{
|
||||
@@ -392,20 +370,11 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
|
||||
if !strings.HasSuffix(r.URL.Path, infoRefsPath) {
|
||||
return fmt.Errorf("http redirect: target %q does not end with %s", r.URL.Path, infoRefsPath)
|
||||
}
|
||||
// A scheme is case-insensitive per RFC 3986, and checkRedirect folds case
|
||||
// when it reads the same hop, so fold here too rather than reject a
|
||||
// spelling that check let through. url.Parse and transport.NewEndpoint
|
||||
// both lowercase what they parse, so only a hand-built URL or Endpoint
|
||||
// arrives uppercased. The folded form is what gets stored below, so every
|
||||
// later request built from the endpoint carries the canonical spelling.
|
||||
scheme := strings.ToLower(r.URL.Scheme)
|
||||
if scheme != "http" && scheme != "https" {
|
||||
if r.URL.Scheme != "http" && r.URL.Scheme != "https" {
|
||||
return fmt.Errorf("http redirect: unsupported scheme %q", r.URL.Scheme)
|
||||
}
|
||||
// schemeUpgrade rather than an inline comparison, so the one cross-scheme
|
||||
// change go-git permits has a single definition shared with
|
||||
// credentialsMayFollow.
|
||||
if !strings.EqualFold(scheme, s.endpoint.Protocol) && !schemeUpgrade(s.endpoint.Protocol, scheme) {
|
||||
if r.URL.Scheme != s.endpoint.Protocol &&
|
||||
!(s.endpoint.Protocol == "http" && r.URL.Scheme == "https") {
|
||||
return fmt.Errorf("http redirect: changes scheme from %q to %q", s.endpoint.Protocol, r.URL.Scheme)
|
||||
}
|
||||
|
||||
@@ -415,20 +384,7 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// The session stores the endpoint and re-applies its credentials on every
|
||||
// later request, so clear them once the redirect has left the origin they
|
||||
// were issued for. This uses the same predicate as stripCredentials, so
|
||||
// both halves share one definition of an origin.
|
||||
//
|
||||
// The two are deliberately asymmetric in one respect: stripCredentials is
|
||||
// sticky over the whole chain, so an origin -> evil -> origin redirect
|
||||
// leaves the discovery GET's later hops unauthenticated even though the
|
||||
// chain returned home. This compares the endpoint only against the final
|
||||
// URL, so the same round trip leaves the session authenticated. That is
|
||||
// not a leak - the final URL's origin is the original one - but it means
|
||||
// such a chain can make the discovery GET anonymous while the session's
|
||||
// POSTs are authenticated, which can surface as a confusing 401.
|
||||
if !credentialsMayFollow(endpointURL(s.endpoint), r.URL) {
|
||||
if host != s.endpoint.Host || effectivePort(r.URL.Scheme, port) != effectivePort(s.endpoint.Protocol, s.endpoint.Port) {
|
||||
s.endpoint.User = ""
|
||||
s.endpoint.Password = ""
|
||||
s.auth = nil
|
||||
@@ -437,7 +393,7 @@ func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
|
||||
s.endpoint.Host = host
|
||||
s.endpoint.Port = port
|
||||
|
||||
s.endpoint.Protocol = scheme
|
||||
s.endpoint.Protocol = r.URL.Scheme
|
||||
s.endpoint.Path = r.URL.Path[:len(r.URL.Path)-len(infoRefsPath)]
|
||||
return nil
|
||||
}
|
||||
@@ -463,132 +419,19 @@ func endpointPort(port string) (int, error) {
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
// schemeUpgrade reports whether the scheme transition from one URL to another
|
||||
// is the one cross-scheme change go-git permits: a plain-http origin upgrading
|
||||
// to https. It strictly improves confidentiality and is how servers steer
|
||||
// clients off cleartext.
|
||||
//
|
||||
// Permitting it at all is a deliberate deviation: curl, git and the Fetch
|
||||
// standard all count scheme as part of host identity and drop credentials on
|
||||
// the upgrade. Auth is sent pre-emptively here, so an http origin has already
|
||||
// spent its credential in cleartext on the first request and refusing the
|
||||
// upgrade would break the clone without unspending it. The host is unchanged,
|
||||
// where an on-path attacker needs a valid certificate to receive anything.
|
||||
func schemeUpgrade(from, to string) bool {
|
||||
return strings.EqualFold(from, "http") && strings.EqualFold(to, "https")
|
||||
}
|
||||
func effectivePort(scheme string, port int) int {
|
||||
if port != 0 {
|
||||
return port
|
||||
}
|
||||
|
||||
// canonicalHost returns u's hostname in the form origins are compared in.
|
||||
//
|
||||
// An address literal is normalised by netip, so the many spellings of one
|
||||
// address are one origin. Two literals are the same origin exactly when netip
|
||||
// parses them to the same Addr, which is also how the WHATWG URL Standard
|
||||
// compares hosts. An IPv4-mapped literal is deliberately not unmapped onto
|
||||
// the IPv4 it dials: reaching the same endpoint is not the same authority,
|
||||
// since net/http sends the literal as written in Host and a server may route
|
||||
// the two spellings to different virtual hosts.
|
||||
//
|
||||
// netip also keeps a scope zone verbatim, which is what origin comparison
|
||||
// needs: net resolves a zone to an interface by exact name, so folding %eth0
|
||||
// onto %ETH0 would call two hosts the same origin that net dials down
|
||||
// different interfaces.
|
||||
//
|
||||
// A registered name is ASCII-lowercased. That fold is the only liberty taken;
|
||||
// every other difference in spelling is a different origin.
|
||||
//
|
||||
// A trailing root dot is one such difference and is kept, for the same reason
|
||||
// as the IPv4-mapped literal: curl and the WHATWG URL Standard both hold
|
||||
// "example.com." and "example.com" to be distinct hosts, and although
|
||||
// crypto/tls and crypto/x509 fold the dot when they authenticate the peer,
|
||||
// net/http sends the name as written in Host.
|
||||
//
|
||||
// The fold is deliberately ASCII-only. strings.ToLower and strings.EqualFold
|
||||
// apply Unicode case mapping, which folds U+03C2 onto U+03C3 and so would
|
||||
// call two hosts the same origin when they resolve to different servers. An
|
||||
// ASCII-only fold cannot merge two names DNS keeps apart.
|
||||
//
|
||||
// No IDNA mapping is applied either, so a unicode hostname is a different
|
||||
// origin from the punycode encoding of it, and from another Unicode case of
|
||||
// itself, even though all three reach the same server. Mapping through
|
||||
// golang.org/x/net/idna would join them, but it can only widen this equality,
|
||||
// never narrow it, so leaving it out can cost a credential across such a
|
||||
// redirect and cannot forward one. Against that cost, go-git pins x/net while
|
||||
// net/http uses the copy vendored into the toolchain: the two are versioned
|
||||
// separately, so a release that moves the Unicode tables under one and not
|
||||
// the other would have this merge origins net/http still dials apart. That is
|
||||
// the failure this comparison exists to prevent, and comparing bytes has no
|
||||
// such mode.
|
||||
func canonicalHost(u *url.URL) string {
|
||||
host := u.Hostname()
|
||||
if addr, err := netip.ParseAddr(host); err == nil {
|
||||
return addr.String()
|
||||
switch strings.ToLower(scheme) {
|
||||
case "http":
|
||||
return 80
|
||||
case "https":
|
||||
return 443
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
b := []byte(host)
|
||||
for i := range b {
|
||||
if b[i] >= 'A' && b[i] <= 'Z' {
|
||||
b[i] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// effectivePort returns u's port as the connection will use it: the scheme's
|
||||
// well-known port when the URL does not spell one out, and without leading
|
||||
// zeroes, so "https://x", "https://x:443" and "https://x:0443" all agree.
|
||||
func effectivePort(u *url.URL) string {
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http":
|
||||
return "80"
|
||||
case "https":
|
||||
return "443"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if trimmed := strings.TrimLeft(port, "0"); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
// credentialsMayFollow reports whether credentials issued for one URL may be
|
||||
// sent to another.
|
||||
//
|
||||
// The relation is deliberately asymmetric: scheme, host and effective port
|
||||
// must all match, except that a plain http origin may upgrade to https on the
|
||||
// same host (see schemeUpgrade). That exception is confined to the two
|
||||
// default ports: 80 to 443 is the upgrade servers actually steer clients
|
||||
// through, whereas a non-default port carries no such convention, so
|
||||
// http://host:8080 to https://host:8443 is a move to another origin like any
|
||||
// other port change.
|
||||
//
|
||||
// Host matching is exact. Unlike Go's http.Client, which forwards credentials
|
||||
// from a host to any subdomain of it, a subdomain is a different origin here —
|
||||
// matching canonical git and libcurl.
|
||||
func credentialsMayFollow(from, to *url.URL) bool {
|
||||
if canonicalHost(from) != canonicalHost(to) {
|
||||
return false
|
||||
}
|
||||
if strings.EqualFold(from.Scheme, to.Scheme) {
|
||||
return effectivePort(from) == effectivePort(to)
|
||||
}
|
||||
return schemeUpgrade(from.Scheme, to.Scheme) &&
|
||||
effectivePort(from) == "80" && effectivePort(to) == "443"
|
||||
}
|
||||
|
||||
// endpointURL renders an Endpoint's origin as a URL, so that the session's
|
||||
// credential clearing and the per-hop stripping share one definition of an
|
||||
// origin and cannot drift apart.
|
||||
func endpointURL(ep *transport.Endpoint) *url.URL {
|
||||
host := strings.Trim(ep.Host, "[]")
|
||||
if ep.Port != 0 {
|
||||
host = net.JoinHostPort(host, strconv.Itoa(ep.Port))
|
||||
} else if strings.Contains(host, ":") {
|
||||
host = "[" + host + "]"
|
||||
}
|
||||
return &url.URL{Scheme: ep.Protocol, Host: host}
|
||||
}
|
||||
|
||||
func (c *client) cloneHTTPClient(transport http.RoundTripper) *http.Client {
|
||||
@@ -605,215 +448,26 @@ func wrapCheckRedirect(policy RedirectPolicy, next func(*http.Request, []*http.R
|
||||
if err := checkRedirect(req, via, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
// Strip before the caller's hook so it observes what will actually
|
||||
// be sent, and again afterwards so a hook of the common "preserve
|
||||
// my headers across redirects" shape - which copies from via[0],
|
||||
// the original unsanitized request - cannot reinstate them.
|
||||
// Carrying credentials across an origin boundary is deliberately
|
||||
// unsupported.
|
||||
stripCredentials(req, via)
|
||||
if next != nil {
|
||||
if err := next(req, via); err != nil {
|
||||
return err
|
||||
}
|
||||
return next(req, via)
|
||||
}
|
||||
stripCredentials(req, via)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// safeHeaders lists the headers go-git sets itself, none of which can carry a
|
||||
// caller credential. stripCredentials keeps only these when a redirect leaves
|
||||
// the credential's origin. Adding a name here makes it forwardable across an
|
||||
// origin boundary - do not add anything a caller can put a secret in.
|
||||
//
|
||||
// This narrows rather than eliminates the exposure: an AuthMethod that writes
|
||||
// a credential into one of these names directly - for example
|
||||
// Header.Set("User-Agent", "token "+secret) - still survives a cross-origin
|
||||
// redirect. Such a value is also sent to the origin and to any proxy in path,
|
||||
// so it should not be placed there whether or not a redirect follows.
|
||||
var safeHeaders = map[string]struct{}{
|
||||
"User-Agent": {},
|
||||
"Host": {},
|
||||
"Accept": {},
|
||||
"Content-Type": {},
|
||||
"Content-Length": {},
|
||||
}
|
||||
|
||||
func filterHeaders(h http.Header) http.Header {
|
||||
filtered := make(http.Header)
|
||||
for key, values := range h {
|
||||
if _, ok := safeHeaders[http.CanonicalHeaderKey(key)]; ok {
|
||||
filtered[key] = values
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// stripCredentials removes credentials from req once the redirect chain has
|
||||
// left the origin of the original, credential-bearing request.
|
||||
//
|
||||
// CheckRedirect is the only hook that runs while a redirected request's
|
||||
// headers are still mutable: http.Client.Do performs the entire chain
|
||||
// internally, so anything the transport does after Do returns - including
|
||||
// ModifyEndpointIfRedirect - is too late for the hops themselves.
|
||||
//
|
||||
// Two subtleties:
|
||||
//
|
||||
// - net/http rebuilds every redirect request from the original request's
|
||||
// headers before calling this, so a header removed at one hop reappears
|
||||
// at the next. The decision is therefore recomputed per hop.
|
||||
// - The decision is sticky: once the chain has left the origin, credentials
|
||||
// stay gone even if a later hop returns to it. Stickiness is derived from
|
||||
// via rather than stored, because this closure is shared across a
|
||||
// session's requests.
|
||||
//
|
||||
// Stripping keeps only the headers go-git sets itself (safeHeaders). An
|
||||
// allowlist is used rather than a list of credential header names because
|
||||
// caller credentials arrive under names that cannot be enumerated -
|
||||
// PRIVATE-TOKEN, X-Api-Key, gateway headers - which is exactly what
|
||||
// net/http's fixed list of sensitive header names gets wrong. It is also
|
||||
// immune to header-name canonicalisation: an AuthMethod that writes a raw map
|
||||
// key is still removed.
|
||||
func stripCredentials(req *http.Request, via []*http.Request) {
|
||||
if len(via) == 0 {
|
||||
return
|
||||
}
|
||||
// net/http sets a URL on every request it builds, and req.URL is non-nil
|
||||
// by construction: checkRedirect dereferences req.URL.Scheme on each path
|
||||
// that returns nil, so it runs first or not at all. This nil check and
|
||||
// the two in crossedOrigin are defensive, against a synthetic caller.
|
||||
// Each treats a URL it cannot read as an origin crossing; removing one
|
||||
// panics in canonicalHost rather than leaking.
|
||||
if origin := via[0].URL; origin != nil && !crossedOrigin(origin, req, via) {
|
||||
return
|
||||
}
|
||||
req.Header = filterHeaders(req.Header)
|
||||
if req.URL != nil {
|
||||
req.URL.User = nil
|
||||
}
|
||||
}
|
||||
|
||||
// crossedOrigin reports whether any hop so far, including the pending one, has
|
||||
// left origin.
|
||||
func crossedOrigin(origin *url.URL, req *http.Request, via []*http.Request) bool {
|
||||
if req.URL == nil || !credentialsMayFollow(origin, req.URL) {
|
||||
return true
|
||||
}
|
||||
for _, prev := range via[1:] {
|
||||
if prev.URL == nil || !credentialsMayFollow(origin, prev.URL) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// redactedURL returns the string form of u with the userinfo password
|
||||
// replaced, for use in error messages. (*url.URL).String() renders the
|
||||
// password verbatim, and request URLs are built from the endpoint, which
|
||||
// carries whatever credentials the caller put in the clone URL.
|
||||
func redactedURL(u *url.URL) string {
|
||||
if u == nil {
|
||||
return ""
|
||||
}
|
||||
if u.User == nil {
|
||||
return u.String()
|
||||
}
|
||||
if _, hasPassword := u.User.Password(); !hasPassword {
|
||||
return u.String()
|
||||
}
|
||||
redacted := *u
|
||||
redacted.User = url.UserPassword(u.User.Username(), "REDACTED")
|
||||
return redacted.String()
|
||||
}
|
||||
|
||||
// redactedRawURL is redactedURL for a string that may not parse. Request URLs
|
||||
// are assembled from Endpoint.String(), which re-emits Endpoint.Path raw, so a
|
||||
// path holding a stray percent produces a string url.Parse rejects. url.Parse
|
||||
// reports the input verbatim and applies no redaction of its own; only
|
||||
// http.Client strips a password, and only from errors it raises itself.
|
||||
func redactedRawURL(raw string) string {
|
||||
i := strings.Index(raw, "://")
|
||||
if i < 0 {
|
||||
return raw
|
||||
}
|
||||
authority := raw[i+3:]
|
||||
if end := strings.IndexByte(authority, '/'); end >= 0 {
|
||||
authority = authority[:end]
|
||||
}
|
||||
at := strings.LastIndexByte(authority, '@')
|
||||
if at < 0 {
|
||||
return raw
|
||||
}
|
||||
colon := strings.IndexByte(authority[:at], ':')
|
||||
if colon < 0 {
|
||||
// Username only, left alone, as redactedURL leaves it.
|
||||
return raw
|
||||
}
|
||||
return raw[:i+3+colon+1] + "REDACTED" + raw[i+3+at:]
|
||||
}
|
||||
|
||||
// newRequest wraps http.NewRequest so that a URL it cannot parse does not
|
||||
// reach the caller with the endpoint's credentials still in it.
|
||||
func newRequest(method, rawURL string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, rawURL, body)
|
||||
if err != nil {
|
||||
var uerr *url.Error
|
||||
if errors.As(err, &uerr) {
|
||||
uerr.URL = redactedRawURL(uerr.URL)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func checkRedirect(req *http.Request, via []*http.Request, policy RedirectPolicy) error {
|
||||
// CheckRedirect is the only hook that runs before the next hop leaves
|
||||
// the client. ModifyEndpointIfRedirect inspects the chain after
|
||||
// client.Do has followed all of it, so a hop rejected there has already
|
||||
// carried the request headers to its server.
|
||||
//
|
||||
// The wording matches the message ModifyEndpointIfRedirect produces for
|
||||
// the same hop, which this check reaches first.
|
||||
if len(via) != 0 {
|
||||
// A hop whose scheme cannot be read cannot be shown not to have been
|
||||
// https, so it is assumed to have been, and a cleartext target is
|
||||
// rejected. Skipping the comparison instead would let an
|
||||
// undeterminable hop turn the check off, which is the wrong default
|
||||
// for a credential control; crossedOrigin fails closed the same way.
|
||||
// An empty scheme is as unreadable as a nil URL, so both take the
|
||||
// assumed-https default.
|
||||
//
|
||||
// The comparisons fold case because a scheme is case-insensitive per
|
||||
// RFC 3986. net/url lowercases what it parses, so only a hand-built
|
||||
// URL reaches here uppercased - the same synthetic caller the nil
|
||||
// checks guard against - and for that caller "HTTPS" to "http" is
|
||||
// still a downgrade.
|
||||
prevScheme := "https"
|
||||
if prev := via[len(via)-1]; prev.URL != nil && prev.URL.Scheme != "" {
|
||||
prevScheme = prev.URL.Scheme
|
||||
}
|
||||
if strings.EqualFold(prevScheme, "https") && strings.EqualFold(req.URL.Scheme, "http") {
|
||||
return fmt.Errorf("http redirect: changes scheme from %q to %q: %s",
|
||||
prevScheme, req.URL.Scheme, redactedURL(req.URL))
|
||||
}
|
||||
}
|
||||
|
||||
switch policy {
|
||||
case FollowRedirects:
|
||||
case NoFollowRedirects:
|
||||
return fmt.Errorf("http redirect: redirects disabled to %s", redactedURL(req.URL))
|
||||
return fmt.Errorf("http redirect: redirects disabled to %s", req.URL)
|
||||
case "", FollowInitialRedirects:
|
||||
if !isInitialRequest(req) {
|
||||
return fmt.Errorf("http redirect: redirect on non-initial request to %s", redactedURL(req.URL))
|
||||
return fmt.Errorf("http redirect: redirect on non-initial request to %s", req.URL)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("http redirect: invalid redirect policy %q", policy)
|
||||
}
|
||||
// Folded for the same reason as the guard above: a scheme is
|
||||
// case-insensitive per RFC 3986, so a spelling the downgrade check read
|
||||
// as https must not be rejected here as a scheme go-git cannot speak.
|
||||
if !strings.EqualFold(req.URL.Scheme, "http") && !strings.EqualFold(req.URL.Scheme, "https") {
|
||||
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
||||
return fmt.Errorf("http redirect: unsupported scheme %q", req.URL.Scheme)
|
||||
}
|
||||
if len(via) >= 10 {
|
||||
@@ -827,17 +481,6 @@ func (*session) Close() error {
|
||||
}
|
||||
|
||||
// AuthMethod is concrete implementation of common.AuthMethod for HTTP services
|
||||
//
|
||||
// Headers SetAuth adds are dropped when a redirect leaves the repository's
|
||||
// origin: only the headers the transport sets itself survive that boundary.
|
||||
// This applies to non-credential headers too, so an implementation that adds a
|
||||
// trace or tenant header loses it on such a hop.
|
||||
//
|
||||
// That filter matches header names, not values. An implementation that writes
|
||||
// a credential into a name the transport also uses - User-Agent, Host, Accept,
|
||||
// Content-Type, Content-Length - has that value carried across the boundary
|
||||
// with the name. Such a credential is also sent to the origin and to any proxy
|
||||
// in path, so it should not be placed there whether or not a redirect follows.
|
||||
type AuthMethod interface {
|
||||
transport.AuthMethod
|
||||
SetAuth(r *http.Request)
|
||||
@@ -955,6 +598,6 @@ func (e *Err) StatusCode() int {
|
||||
|
||||
func (e *Err) Error() string {
|
||||
return fmt.Sprintf("unexpected requesting %q status code: %d",
|
||||
redactedURL(e.Response.Request.URL), e.Response.StatusCode,
|
||||
e.Response.Request.URL, e.Response.StatusCode,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ func (s *rpSession) doRequest(
|
||||
body = content
|
||||
}
|
||||
|
||||
req, err := newRequest(method, url, body)
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, plumbing.NewPermanentError(err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ func (s *upSession) doRequest(
|
||||
body = content
|
||||
}
|
||||
|
||||
req, err := newRequest(method, url, body)
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, plumbing.NewPermanentError(err)
|
||||
}
|
||||
|
||||
-1
@@ -433,7 +433,6 @@ func dotGitCommonDirectory(fs billy.Filesystem) (commonDir billy.Filesystem, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ioutil.CheckClose(f, &err)
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
|
||||
-57
@@ -16,7 +16,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/internal/pathutil"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/storage"
|
||||
@@ -80,52 +79,8 @@ var (
|
||||
// resolve outside the modules/ subtree, mirroring canonical Git's
|
||||
// "ignoring suspicious submodule name" defence.
|
||||
ErrModuleNameEscape = errors.New("submodule name escapes modules/ directory")
|
||||
// ErrReferenceNameEscape is returned when a reference name would
|
||||
// resolve outside its reference sub-tree once turned into a path
|
||||
// under the .git directory (e.g. a name with a ".." component).
|
||||
ErrReferenceNameEscape = errors.New("reference name escapes the reference storage")
|
||||
)
|
||||
|
||||
// isPathSep reports whether r is a path separator in reference names.
|
||||
// It treats both '/' and '\\' as separators to harden against cross-OS paths.
|
||||
func isPathSep(r rune) bool { return r == '/' || r == '\\' }
|
||||
|
||||
// validReferenceName rejects reference names that cannot be safely turned into
|
||||
// a path under the .git directory. A loose reference is stored verbatim at
|
||||
// ".git/<name>", so a crafted name — for instance one advertised by a malicious
|
||||
// remote — could climb out of its reference sub-tree and read, overwrite, or
|
||||
// delete unrelated metadata such as .git/config.
|
||||
//
|
||||
// The storage-safety gate is plumbing.ReferenceName.IsSafe, mirroring Git's
|
||||
// refname_is_safe: a name must be under refs/ without escaping it, or be a
|
||||
// [A-Z_] pseudo-ref. This alone rejects absolute, drive-prefixed, escaping and
|
||||
// single-level metadata names. On top of it, this adds filesystem-specific
|
||||
// hardening that IsSafe's literal check does not cover: control characters, and
|
||||
// components a case-insensitive/NTFS/HFS+ filesystem would fold back to "." or
|
||||
// ".." (trailing dots/spaces, Alternate Data Streams, ignorable Unicode code
|
||||
// points), delegated to pathutil.IsHFSDot and pathutil.IsNTFSDot with "." as
|
||||
// the needle — as validSubmoduleName does — and run regardless of host OS.
|
||||
func validReferenceName(name plumbing.ReferenceName) error {
|
||||
if !name.IsSafe() {
|
||||
return fmt.Errorf("%w: %q is not under refs/ nor a valid pseudo-ref", ErrReferenceNameEscape, string(name))
|
||||
}
|
||||
|
||||
s := string(name)
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < 0x20 || s[i] == 0x7f {
|
||||
return fmt.Errorf("%w: %q", ErrReferenceNameEscape, s)
|
||||
}
|
||||
}
|
||||
for _, part := range strings.FieldsFunc(s, isPathSep) {
|
||||
// IsNTFSDot/IsHFSDot with a "." needle match ".." and its disguises
|
||||
// but not a bare ".", so reject that component explicitly too.
|
||||
if part == "." || pathutil.IsHFSDot(part, ".") || pathutil.IsNTFSDot(part, ".", "") {
|
||||
return fmt.Errorf("%w: %q", ErrReferenceNameEscape, s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options holds configuration for the storage.
|
||||
type Options struct {
|
||||
// ExclusiveAccess means that the filesystem is not modified externally
|
||||
@@ -751,10 +706,6 @@ func (d *DotGit) checkReferenceAndTruncate(f billy.File, old *plumbing.Reference
|
||||
}
|
||||
|
||||
func (d *DotGit) SetRef(r, old *plumbing.Reference) error {
|
||||
if err := validReferenceName(r.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var content string
|
||||
switch r.Type() {
|
||||
case plumbing.SymbolicReference:
|
||||
@@ -790,10 +741,6 @@ func (d *DotGit) Refs() ([]*plumbing.Reference, error) {
|
||||
|
||||
// Ref returns the reference for a given reference name.
|
||||
func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
|
||||
if err := validReferenceName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref, err := d.readReferenceFile(".", name.String())
|
||||
if err == nil {
|
||||
return ref, nil
|
||||
@@ -857,10 +804,6 @@ func (d *DotGit) packedRef(name plumbing.ReferenceName) (*plumbing.Reference, er
|
||||
|
||||
// RemoveRef removes a reference by name.
|
||||
func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
|
||||
if err := validReferenceName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path := d.fs.Join(".", name.String())
|
||||
_, err := d.fs.Stat(path)
|
||||
if err == nil {
|
||||
|
||||
-67
@@ -583,10 +583,6 @@ func (w *Worktree) checkoutChangeSubmodule(name string,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.clearBlockingSymlinks(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.Filesystem.MkdirAll(name, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -630,70 +626,7 @@ func (w *Worktree) checkoutChangeRegularFile(name string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// clearBlockingSymlinks removes a symlink that is in the way of
|
||||
// materialising name, so the checkout writes a real entry in its place
|
||||
// instead of following the link out of the worktree. Two cases:
|
||||
//
|
||||
// - a leading directory component that is a symlink (e.g. "s" while
|
||||
// writing "s/config", where "s" links to ".git"): OpenFile/MkdirAll
|
||||
// would traverse it, so the write would land under the link's target.
|
||||
// - the final component itself being a symlink (e.g. writing "s" while
|
||||
// "s" links to ".git/config"): OpenFile with O_TRUNC, or Symlink,
|
||||
// would follow/replace through it and clobber the target.
|
||||
//
|
||||
// A symlink can never be a legitimate parent of, or the destination for,
|
||||
// a tracked entry, so removing it is always correct. This mirrors upstream
|
||||
// Git's forced checkout, which unlinks a blocking symlink in the leading
|
||||
// path (create_directories) and unlinks an existing entry before
|
||||
// write_entry.
|
||||
// https://github.com/git/git/blob/v2.54.0/entry.c#L50
|
||||
func (w *Worktree) clearBlockingSymlinks(name string) error {
|
||||
var dirs []string
|
||||
for dir := filepath.Dir(name); dir != "." && dir != "" && dir != string(filepath.Separator); dir = filepath.Dir(dir) {
|
||||
dirs = append(dirs, dir)
|
||||
}
|
||||
// Leading components, shallowest-first: removing the shallowest symlink
|
||||
// invalidates every component beneath it, so a single removal is enough.
|
||||
for i := len(dirs) - 1; i >= 0; i-- {
|
||||
fi, err := w.Filesystem.Lstat(dirs[i])
|
||||
if err != nil {
|
||||
// A missing component is created as a real directory by the
|
||||
// checkout. Any other error means we cannot tell whether it is
|
||||
// a symlink, so surface it instead of leaving a blocking link in
|
||||
// place and failing later in a harder-to-diagnose way.
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return w.Filesystem.Remove(dirs[i])
|
||||
}
|
||||
}
|
||||
// Final component: an existing symlink here would be followed by the
|
||||
// subsequent OpenFile/Symlink/MkdirAll, so replace it.
|
||||
fi, err := w.Filesystem.Lstat(name)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return w.Filesystem.Remove(name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Worktree) checkoutFile(f *object.File) (err error) {
|
||||
// checkoutFile is the materialisation boundary for tracked entries.
|
||||
// Remove any blocking symlink first so the subsequent OpenFile or
|
||||
// Symlink call writes the entry itself instead of following a planted
|
||||
// final-component link in the underlying filesystem.
|
||||
if err := w.clearBlockingSymlinks(f.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mode, err := f.Mode.ToOSFileMode()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
+15
-100
@@ -35,25 +35,10 @@ func defaultProtectNTFS() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// worktreeFilesystem wraps a billy.Filesystem and validates every path it
|
||||
// is handed, so worktree operations cannot use dangerous paths at the
|
||||
// boundary. Two layers apply:
|
||||
//
|
||||
// - validPath rejects dangerous path *strings*: .git and its HFS+/NTFS
|
||||
// variants, "..", control characters, volume names.
|
||||
// - validNoLeadingSymlink rejects paths whose leading directories
|
||||
// already exist on disk as symlinks, so a write or delete cannot
|
||||
// follow a planted link out of the tree.
|
||||
//
|
||||
// Both layers run on every mutating operation (validWritePath) and every
|
||||
// read (validReadPath). Chroot additionally refuses a symlink as the final
|
||||
// component, so a sub-filesystem such as a submodule worktree cannot be
|
||||
// scoped to a redirected target.
|
||||
//
|
||||
// The wrapper intentionally stops at leading-component traversal. Callers
|
||||
// that need final-component no-follow semantics for materialisation
|
||||
// (checkoutFile) enforce that directly by removing the blocking symlink
|
||||
// before opening the destination path.
|
||||
// worktreeFilesystem wraps a billy.Filesystem and validates every path passed
|
||||
// to a mutating operation. This prevents writing to, or deleting from,
|
||||
// dangerous locations (e.g. .git/*, ../) regardless of which worktree
|
||||
// code path triggers the operation.
|
||||
type worktreeFilesystem struct {
|
||||
billy.Filesystem
|
||||
protectNTFS bool
|
||||
@@ -65,7 +50,7 @@ func newWorktreeFilesystem(fs billy.Filesystem, protectNTFS, protectHFS bool) *w
|
||||
}
|
||||
|
||||
func (sfs *worktreeFilesystem) Create(filename string) (billy.File, error) {
|
||||
if err := sfs.validWritePath(filename); err != nil {
|
||||
if err := sfs.validPath(filename); err != nil {
|
||||
return nil, fmt.Errorf("create: %w", err)
|
||||
}
|
||||
return sfs.Filesystem.Create(filename)
|
||||
@@ -79,7 +64,7 @@ func (sfs *worktreeFilesystem) Open(filename string) (billy.File, error) {
|
||||
}
|
||||
|
||||
func (sfs *worktreeFilesystem) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) {
|
||||
if err := sfs.validWritePath(filename); err != nil {
|
||||
if err := sfs.validPath(filename); err != nil {
|
||||
return nil, fmt.Errorf("openfile: %w", err)
|
||||
}
|
||||
return sfs.Filesystem.OpenFile(filename, flag, perm)
|
||||
@@ -93,14 +78,14 @@ func (sfs *worktreeFilesystem) Stat(filename string) (os.FileInfo, error) {
|
||||
}
|
||||
|
||||
func (sfs *worktreeFilesystem) Remove(filename string) error {
|
||||
if err := sfs.validWritePath(filename); err != nil {
|
||||
if err := sfs.validPath(filename); err != nil {
|
||||
return fmt.Errorf("remove: %w", err)
|
||||
}
|
||||
return sfs.Filesystem.Remove(filename)
|
||||
}
|
||||
|
||||
func (sfs *worktreeFilesystem) Rename(from, to string) error {
|
||||
if err := sfs.validWritePath(from, to); err != nil {
|
||||
if err := sfs.validPath(from, to); err != nil {
|
||||
return fmt.Errorf("rename: %w", err)
|
||||
}
|
||||
return sfs.Filesystem.Rename(from, to)
|
||||
@@ -121,7 +106,7 @@ func (sfs *worktreeFilesystem) Lstat(filename string) (os.FileInfo, error) {
|
||||
}
|
||||
|
||||
func (sfs *worktreeFilesystem) Symlink(target, link string) error {
|
||||
if err := sfs.validWritePath(link); err != nil {
|
||||
if err := sfs.validPath(link); err != nil {
|
||||
return fmt.Errorf("symlink: %w", err)
|
||||
}
|
||||
if err := sfs.validSymlinkName(link); err != nil {
|
||||
@@ -146,7 +131,7 @@ func (sfs *worktreeFilesystem) MkdirAll(path string, perm os.FileMode) error {
|
||||
if path == "" || path == "." || path == "/" {
|
||||
return nil
|
||||
}
|
||||
if err := sfs.validWritePath(path); err != nil {
|
||||
if err := sfs.validPath(path); err != nil {
|
||||
return fmt.Errorf("mkdirall: %w", err)
|
||||
}
|
||||
return sfs.Filesystem.MkdirAll(path, perm)
|
||||
@@ -160,39 +145,18 @@ func (sfs *worktreeFilesystem) Chroot(path string) (billy.Filesystem, error) {
|
||||
if err := sfs.validReadPath(path); err != nil {
|
||||
return nil, fmt.Errorf("chroot: %w", err)
|
||||
}
|
||||
// Chroot scopes a sub-filesystem to path, so the final component must
|
||||
// be a real directory too: a symlink there would silently redirect the
|
||||
// scope (e.g. a submodule worktree) to a target outside the tree. This
|
||||
// is the "valid path, wrong target" case that validNoLeadingSymlink,
|
||||
// which only inspects leading components, does not cover.
|
||||
//
|
||||
// A non-existent target is fine: Chroot creates it as a real
|
||||
// directory. Any other Lstat error means we cannot prove the target
|
||||
// is not a symlink, so fail closed rather than scope through it.
|
||||
if fi, err := sfs.Filesystem.Lstat(path); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("chroot: cannot stat %q: %w", path, err)
|
||||
}
|
||||
} else if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, fmt.Errorf("chroot: invalid path %q: is a symlink", path)
|
||||
}
|
||||
return sfs.Filesystem.Chroot(path)
|
||||
}
|
||||
|
||||
// validReadPath is like validWritePath but treats the empty string and "."
|
||||
// as valid references to the worktree root. Read-side operations on the
|
||||
// root (e.g. ReadDir(""), Lstat(".")) are legitimate. Mutating the root
|
||||
// itself is not, so write-side operations reject it via validPath. Reads
|
||||
// are still refused through a leading symlink, so the wrapper never
|
||||
// follows a planted link even on the read surface.
|
||||
// validReadPath is like validPath but treats the empty string and "." as
|
||||
// valid references to the worktree root. Read-side operations on the root
|
||||
// (e.g. ReadDir(""), Lstat(".")) are legitimate; mutating the root itself
|
||||
// is not, so write-side operations continue to use validPath directly.
|
||||
func (sfs *worktreeFilesystem) validReadPath(p string) error {
|
||||
if p == "" || p == "." || p == "/" {
|
||||
return nil
|
||||
}
|
||||
if err := sfs.validPath(p); err != nil {
|
||||
return err
|
||||
}
|
||||
return sfs.validNoLeadingSymlink(p)
|
||||
return sfs.validPath(p)
|
||||
}
|
||||
|
||||
var errUnsupportedOperation = errors.New("unsupported operation")
|
||||
@@ -268,55 +232,6 @@ func (sfs *worktreeFilesystem) validPath(paths ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validWritePath validates paths for mutating operations. It layers the
|
||||
// filesystem-state check validNoLeadingSymlink on top of the string-only
|
||||
// checks in validPath, so a write can neither name a dangerous path nor
|
||||
// reach one by traversing an existing symlink. Every mutating method on
|
||||
// the wrapper funnels through here, so the leading-symlink invariant holds
|
||||
// for all worktree writers without each call site having to remember it.
|
||||
func (sfs *worktreeFilesystem) validWritePath(paths ...string) error {
|
||||
if err := sfs.validPath(paths...); err != nil {
|
||||
return err
|
||||
}
|
||||
return sfs.validNoLeadingSymlink(paths...)
|
||||
}
|
||||
|
||||
// validNoLeadingSymlink rejects paths whose leading directory components
|
||||
// resolve through a symlink that already exists on the underlying
|
||||
// filesystem. validPath guards the path string. This guards the on-disk
|
||||
// state, so a write or delete cannot reach outside the worktree by
|
||||
// traversing a symlink that a tree or an earlier step left in place.
|
||||
//
|
||||
// This is the fail-closed backstop for the whole class. Callers that want
|
||||
// upstream's replace-and-continue behaviour (checkout) remove the blocking
|
||||
// symlink first via clearBlockingSymlinks, so no symlink remains when the
|
||||
// write reaches the wrapper. Callers that do not get a safe error,
|
||||
// matching upstream Git refusing rather than following the link. See
|
||||
// has_symlink_leading_path (symlinks.c) and the check_leading_path guard
|
||||
// in unlink_entry (entry.c).
|
||||
func (sfs *worktreeFilesystem) validNoLeadingSymlink(paths ...string) error {
|
||||
for _, p := range paths {
|
||||
for dir := filepath.Dir(p); dir != "." && dir != "" && dir != string(filepath.Separator); dir = filepath.Dir(dir) {
|
||||
fi, err := sfs.Filesystem.Lstat(dir)
|
||||
if err != nil {
|
||||
// A missing ancestor is materialised as a real directory,
|
||||
// so it cannot be a symlink and is safe to skip. Any other
|
||||
// error (permission, I/O) means we cannot prove the
|
||||
// component is not a symlink, so fail closed rather than
|
||||
// let the operation traverse an unverified component.
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("invalid path %q: cannot stat leading component %q: %w", p, dir, err)
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("invalid path %q: leading component %q is a symlink", p, dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validSymlinkName checks the per-component name of a symlink for
|
||||
// dotfile names that attackers can use to trick a checkout into
|
||||
// writing a dangerous symlink. Each path component is compared
|
||||
|
||||
+1
-1
@@ -371,7 +371,7 @@ func (w *Worktree) doAdd(path string, ignorePattern []gitignore.Pattern, skipSta
|
||||
}
|
||||
}
|
||||
|
||||
path = filepath.ToSlash(filepath.Clean(path))
|
||||
path = filepath.Clean(path)
|
||||
|
||||
if err != nil || !fi.IsDir() {
|
||||
added, h, err = w.doAddFile(idx, s, path, ignorePattern)
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
version: 2
|
||||
|
||||
builds:
|
||||
-
|
||||
id: "cpuid"
|
||||
binary: cpuid
|
||||
main: ./cmd/cpuid/main.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -ldflags=-s -w
|
||||
goos:
|
||||
- aix
|
||||
- linux
|
||||
- freebsd
|
||||
- netbsd
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
|
||||
archives:
|
||||
-
|
||||
id: cpuid
|
||||
name_template: "cpuid-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^doc:'
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^tests:'
|
||||
- '^Update\sREADME.md'
|
||||
|
||||
nfpms:
|
||||
-
|
||||
file_name_template: "cpuid_package_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
vendor: Klaus Post
|
||||
homepage: https://github.com/klauspost/cpuid
|
||||
maintainer: Klaus Post <klauspost@gmail.com>
|
||||
description: CPUID Tool
|
||||
license: BSD 3-Clause
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2015- Klaus Post & Contributors.
|
||||
Email: klauspost@gmail.com
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Klaus Post
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
# cpuid
|
||||
Package cpuid provides information about the CPU running the current program.
|
||||
|
||||
CPU features are detected on startup, and kept for fast access through the life of the application.
|
||||
Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
|
||||
|
||||
You can access the CPU information by accessing the shared CPU variable of the cpuid library.
|
||||
|
||||
Package home: https://github.com/klauspost/cpuid
|
||||
|
||||
[](https://pkg.go.dev/github.com/klauspost/cpuid/v2)
|
||||
[](https://github.com/klauspost/cpuid/actions/workflows/go.yml)
|
||||
|
||||
## installing
|
||||
|
||||
`go get -u github.com/klauspost/cpuid/v2` using modules.
|
||||
Drop `v2` for others.
|
||||
|
||||
Installing binary:
|
||||
|
||||
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
|
||||
|
||||
Or download binaries from release page: https://github.com/klauspost/cpuid/releases
|
||||
|
||||
### Homebrew
|
||||
|
||||
For macOS/Linux users, you can install via [brew](https://brew.sh/)
|
||||
|
||||
```sh
|
||||
$ brew install cpuid
|
||||
```
|
||||
|
||||
## example
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Print basic CPU information:
|
||||
fmt.Println("Name:", CPU.BrandName)
|
||||
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
|
||||
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
|
||||
fmt.Println("LogicalCores:", CPU.LogicalCores)
|
||||
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
|
||||
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
|
||||
fmt.Println("Cacheline bytes:", CPU.CacheLine)
|
||||
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
|
||||
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
|
||||
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
|
||||
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
|
||||
fmt.Println("Frequency", CPU.Hz, "hz")
|
||||
|
||||
// Test if we have these specific features:
|
||||
if CPU.Supports(SSE, SSE2) {
|
||||
fmt.Println("We have Streaming SIMD 2 Extensions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sample output:
|
||||
```
|
||||
>go run main.go
|
||||
Name: AMD Ryzen 9 3950X 16-Core Processor
|
||||
PhysicalCores: 16
|
||||
ThreadsPerCore: 2
|
||||
LogicalCores: 32
|
||||
Family 23 Model: 113 Vendor ID: AMD
|
||||
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CMOV,CX16,F16C,FMA3,HTT,HYPERVISOR,LZCNT,MMX,MMXEXT,NX,POPCNT,RDRAND,RDSEED,RDTSCP,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3
|
||||
Cacheline bytes: 64
|
||||
L1 Data Cache: 32768 bytes
|
||||
L1 Instruction Cache: 32768 bytes
|
||||
L2 Cache: 524288 bytes
|
||||
L3 Cache: 16777216 bytes
|
||||
Frequency 0 hz
|
||||
We have Streaming SIMD 2 Extensions
|
||||
```
|
||||
|
||||
# usage
|
||||
|
||||
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
|
||||
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
|
||||
|
||||
To test a larger number of features, they can be combined using `f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)`, etc.
|
||||
This can be using with `cpuid.CPU.HasAll(f)` to quickly test if all features are supported.
|
||||
|
||||
Note that for some cpu/os combinations some features will not be detected.
|
||||
`amd64` has rather good support and should work reliably on all platforms.
|
||||
|
||||
Note that hypervisors may not pass through all CPU features through to the guest OS,
|
||||
so even if your host supports a feature it may not be visible on guests.
|
||||
|
||||
## arm64 feature detection
|
||||
|
||||
Not all operating systems provide ARM features directly
|
||||
and there is no safe way to do so for the rest.
|
||||
|
||||
Currently `arm64/linux` and `arm64/freebsd` should be quite reliable.
|
||||
`arm64/darwin` adds features expected from the M1 processor, but a lot remains undetected.
|
||||
|
||||
A `DetectARM()` can be used if you are able to control your deployment,
|
||||
it will detect CPU features, but may crash if the OS doesn't intercept the calls.
|
||||
A `-cpu.arm` flag for detecting unsafe ARM features can be added. See below.
|
||||
|
||||
Note that currently only features are detected on ARM,
|
||||
no additional information is currently available.
|
||||
|
||||
## flags
|
||||
|
||||
It is possible to add flags that affects cpu detection.
|
||||
|
||||
For this the `Flags()` command is provided.
|
||||
|
||||
This must be called *before* `flag.Parse()` AND after the flags have been parsed `Detect()` must be called.
|
||||
|
||||
This means that any detection used in `init()` functions will not contain these flags.
|
||||
|
||||
Example:
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cpuid.Flags()
|
||||
flag.Parse()
|
||||
cpuid.Detect()
|
||||
|
||||
// Test if we have these specific features:
|
||||
if cpuid.CPU.Supports(cpuid.SSE, cpuid.SSE2) {
|
||||
fmt.Println("We have Streaming SIMD 2 Extensions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## commandline
|
||||
|
||||
Download as binary from: https://github.com/klauspost/cpuid/releases
|
||||
|
||||
Install from source:
|
||||
|
||||
`go install github.com/klauspost/cpuid/v2/cmd/cpuid@latest`
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
λ cpuid
|
||||
Name: AMD Ryzen 9 3950X 16-Core Processor
|
||||
Vendor String: AuthenticAMD
|
||||
Vendor ID: AMD
|
||||
PhysicalCores: 16
|
||||
Threads Per Core: 2
|
||||
Logical Cores: 32
|
||||
CPU Family 23 Model: 113
|
||||
Features: ADX,AESNI,AVX,AVX2,BMI1,BMI2,CLMUL,CLZERO,CMOV,CMPXCHG8,CPBOOST,CX16,F16C,FMA3,FXSR,FXSROPT,HTT,HYPERVISOR,LAHF,LZCNT,MCAOVERFLOW,MMX,MMXEXT,MOVBE,NX,OSXSAVE,POPCNT,RDRAND,RDSEED,RDTSCP,SCE,SHA,SSE,SSE2,SSE3,SSE4,SSE42,SSE4A,SSSE3,SUCCOR,X87,XSAVE
|
||||
Microarchitecture level: 3
|
||||
Cacheline bytes: 64
|
||||
L1 Instruction Cache: 32768 bytes
|
||||
L1 Data Cache: 32768 bytes
|
||||
L2 Cache: 524288 bytes
|
||||
L3 Cache: 16777216 bytes
|
||||
|
||||
```
|
||||
### JSON Output:
|
||||
|
||||
```
|
||||
λ cpuid --json
|
||||
{
|
||||
"BrandName": "AMD Ryzen 9 3950X 16-Core Processor",
|
||||
"VendorID": 2,
|
||||
"VendorString": "AuthenticAMD",
|
||||
"PhysicalCores": 16,
|
||||
"ThreadsPerCore": 2,
|
||||
"LogicalCores": 32,
|
||||
"Family": 23,
|
||||
"Model": 113,
|
||||
"CacheLine": 64,
|
||||
"Hz": 0,
|
||||
"BoostFreq": 0,
|
||||
"Cache": {
|
||||
"L1I": 32768,
|
||||
"L1D": 32768,
|
||||
"L2": 524288,
|
||||
"L3": 16777216
|
||||
},
|
||||
"SGX": {
|
||||
"Available": false,
|
||||
"LaunchControl": false,
|
||||
"SGX1Supported": false,
|
||||
"SGX2Supported": false,
|
||||
"MaxEnclaveSizeNot64": 0,
|
||||
"MaxEnclaveSize64": 0,
|
||||
"EPCSections": null
|
||||
},
|
||||
"Features": [
|
||||
"ADX",
|
||||
"AESNI",
|
||||
"AVX",
|
||||
"AVX2",
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CLZERO",
|
||||
"CMOV",
|
||||
"CMPXCHG8",
|
||||
"CPBOOST",
|
||||
"CX16",
|
||||
"F16C",
|
||||
"FMA3",
|
||||
"FXSR",
|
||||
"FXSROPT",
|
||||
"HTT",
|
||||
"HYPERVISOR",
|
||||
"LAHF",
|
||||
"LZCNT",
|
||||
"MCAOVERFLOW",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"MOVBE",
|
||||
"NX",
|
||||
"OSXSAVE",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SCE",
|
||||
"SHA",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4",
|
||||
"SSE42",
|
||||
"SSE4A",
|
||||
"SSSE3",
|
||||
"SUCCOR",
|
||||
"X87",
|
||||
"XSAVE"
|
||||
],
|
||||
"X64Level": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Check CPU microarch level
|
||||
|
||||
```
|
||||
λ cpuid --check-level=3
|
||||
2022/03/18 17:04:40 AMD Ryzen 9 3950X 16-Core Processor
|
||||
2022/03/18 17:04:40 Microarchitecture level 3 is supported. Max level is 3.
|
||||
Exit Code 0
|
||||
|
||||
λ cpuid --check-level=4
|
||||
2022/03/18 17:06:18 AMD Ryzen 9 3950X 16-Core Processor
|
||||
2022/03/18 17:06:18 Microarchitecture level 4 not supported. Max level is 3.
|
||||
Exit Code 1
|
||||
```
|
||||
|
||||
|
||||
## Available flags
|
||||
|
||||
### x86 & amd64
|
||||
|
||||
| Feature Flag | Description |
|
||||
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ADX | Intel ADX (Multi-Precision Add-Carry Instruction Extensions) |
|
||||
| AESNI | Advanced Encryption Standard New Instructions |
|
||||
| AMD3DNOW | AMD 3DNOW |
|
||||
| AMD3DNOWEXT | AMD 3DNowExt |
|
||||
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
|
||||
| AMXINT8 | Tile computational operations on 8-bit integers |
|
||||
| AMXFP16 | Tile computational operations on FP16 numbers |
|
||||
| AMXFP8 | Tile computational operations on FP8 numbers |
|
||||
| AMXCOMPLEX | Tile computational operations on complex numbers |
|
||||
| AMXTILE | Tile architecture |
|
||||
| AMXTF32 | Matrix Multiplication of TF32 Tiles into Packed Single Precision Tile |
|
||||
| AMXTRANSPOSE | Tile multiply where the first operand is transposed |
|
||||
| APX_F | Intel APX |
|
||||
| AVX | AVX functions |
|
||||
| AVX10 | If set the Intel AVX10 Converged Vector ISA is supported |
|
||||
| AVX10_128 | If set indicates that AVX10 128-bit vector support is present |
|
||||
| AVX10_256 | If set indicates that AVX10 256-bit vector support is present |
|
||||
| AVX10_512 | If set indicates that AVX10 512-bit vector support is present |
|
||||
| AVX2 | AVX2 functions |
|
||||
| AVX512BF16 | AVX-512 BFLOAT16 Instructions |
|
||||
| AVX512BITALG | AVX-512 Bit Algorithms |
|
||||
| AVX512BW | AVX-512 Byte and Word Instructions |
|
||||
| AVX512CD | AVX-512 Conflict Detection Instructions |
|
||||
| AVX512DQ | AVX-512 Doubleword and Quadword Instructions |
|
||||
| AVX512ER | AVX-512 Exponential and Reciprocal Instructions |
|
||||
| AVX512F | AVX-512 Foundation |
|
||||
| AVX512FP16 | AVX-512 FP16 Instructions |
|
||||
| AVX512IFMA | AVX-512 Integer Fused Multiply-Add Instructions |
|
||||
| AVX512PF | AVX-512 Prefetch Instructions |
|
||||
| AVX512VBMI | AVX-512 Vector Bit Manipulation Instructions |
|
||||
| AVX512VBMI2 | AVX-512 Vector Bit Manipulation Instructions, Version 2 |
|
||||
| AVX512VL | AVX-512 Vector Length Extensions |
|
||||
| AVX512VNNI | AVX-512 Vector Neural Network Instructions |
|
||||
| AVX512VP2INTERSECT | AVX-512 Intersect for D/Q |
|
||||
| AVX512VPOPCNTDQ | AVX-512 Vector Population Count Doubleword and Quadword |
|
||||
| AVXIFMA | AVX-IFMA instructions |
|
||||
| AVXNECONVERT | AVX-NE-CONVERT instructions |
|
||||
| AVXSLOW | Indicates the CPU performs 2 128 bit operations instead of one |
|
||||
| AVXVNNI | AVX (VEX encoded) VNNI neural network instructions |
|
||||
| AVXVNNIINT8 | AVX-VNNI-INT8 instructions |
|
||||
| AVXVNNIINT16 | AVX-VNNI-INT16 instructions |
|
||||
| BHI_CTRL | Branch History Injection and Intra-mode Branch Target Injection / CVE-2022-0001, CVE-2022-0002 / INTEL-SA-00598 |
|
||||
| BMI1 | Bit Manipulation Instruction Set 1 |
|
||||
| BMI2 | Bit Manipulation Instruction Set 2 |
|
||||
| CETIBT | Intel CET Indirect Branch Tracking |
|
||||
| CETSS | Intel CET Shadow Stack |
|
||||
| CLDEMOTE | Cache Line Demote |
|
||||
| CLMUL | Carry-less Multiplication |
|
||||
| CLZERO | CLZERO instruction supported |
|
||||
| CMOV | i686 CMOV |
|
||||
| CMPCCXADD | CMPCCXADD instructions |
|
||||
| CMPSB_SCADBS_SHORT | Fast short CMPSB and SCASB |
|
||||
| CMPXCHG8 | CMPXCHG8 instruction |
|
||||
| CPBOOST | Core Performance Boost |
|
||||
| CPPC | AMD: Collaborative Processor Performance Control |
|
||||
| CX16 | CMPXCHG16B Instruction |
|
||||
| EFER_LMSLE_UNS | AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ |
|
||||
| ENQCMD | Enqueue Command |
|
||||
| ERMS | Enhanced REP MOVSB/STOSB |
|
||||
| F16C | Half-precision floating-point conversion |
|
||||
| FLUSH_L1D | Flush L1D cache |
|
||||
| FMA3 | Intel FMA 3. Does not imply AVX. |
|
||||
| FMA4 | Bulldozer FMA4 functions |
|
||||
| FP128 | AMD: When set, the internal FP/SIMD execution datapath is 128-bits wide |
|
||||
| FP256 | AMD: When set, the internal FP/SIMD execution datapath is 256-bits wide |
|
||||
| FSRM | Fast Short Rep Mov |
|
||||
| FXSR | FXSAVE, FXRESTOR instructions, CR4 bit 9 |
|
||||
| FXSROPT | FXSAVE/FXRSTOR optimizations |
|
||||
| GFNI | Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage. |
|
||||
| HLE | Hardware Lock Elision |
|
||||
| HRESET | If set CPU supports history reset and the IA32_HRESET_ENABLE MSR |
|
||||
| HTT | Hyperthreading (enabled) |
|
||||
| HWA | Hardware assert supported. Indicates support for MSRC001_10 |
|
||||
| HYBRID_CPU | This part has CPUs of more than one type. |
|
||||
| HYPERVISOR | This bit has been reserved by Intel & AMD for use by hypervisors |
|
||||
| IA32_ARCH_CAP | IA32_ARCH_CAPABILITIES MSR (Intel) |
|
||||
| IA32_CORE_CAP | IA32_CORE_CAPABILITIES MSR |
|
||||
| IBPB | Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) |
|
||||
| IBRS | AMD: Indirect Branch Restricted Speculation |
|
||||
| IBRS_PREFERRED | AMD: IBRS is preferred over software solution |
|
||||
| IBRS_PROVIDES_SMP | AMD: IBRS provides Same Mode Protection |
|
||||
| IBS | Instruction Based Sampling (AMD) |
|
||||
| IBSBRNTRGT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSFETCHSAM | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSFFV | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPCNT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPCNTEXT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSOPSAM | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSRDWROPCNT | Instruction Based Sampling Feature (AMD) |
|
||||
| IBSRIPINVALIDCHK | Instruction Based Sampling Feature (AMD) |
|
||||
| IBS_FETCH_CTLX | AMD: IBS fetch control extended MSR supported |
|
||||
| IBS_OPDATA4 | AMD: IBS op data 4 MSR supported |
|
||||
| IBS_OPFUSE | AMD: Indicates support for IbsOpFuse |
|
||||
| IBS_PREVENTHOST | Disallowing IBS use by the host supported |
|
||||
| IBS_ZEN4 | Fetch and Op IBS support IBS extensions added with Zen4 |
|
||||
| IDPRED_CTRL | IPRED_DIS |
|
||||
| INT_WBINVD | WBINVD/WBNOINVD are interruptible. |
|
||||
| INVLPGB | NVLPGB and TLBSYNC instruction supported |
|
||||
| KEYLOCKER | Key locker |
|
||||
| KEYLOCKERW | Key locker wide |
|
||||
| LAHF | LAHF/SAHF in long mode |
|
||||
| LAM | If set, CPU supports Linear Address Masking |
|
||||
| LBRVIRT | LBR virtualization |
|
||||
| LZCNT | LZCNT instruction |
|
||||
| MCAOVERFLOW | MCA overflow recovery support. |
|
||||
| MCDT_NO | Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it. |
|
||||
| MCOMMIT | MCOMMIT instruction supported |
|
||||
| MD_CLEAR | VERW clears CPU buffers |
|
||||
| MMX | standard MMX |
|
||||
| MMXEXT | SSE integer functions or AMD MMX ext |
|
||||
| MOVBE | MOVBE instruction (big-endian) |
|
||||
| MOVDIR64B | Move 64 Bytes as Direct Store |
|
||||
| MOVDIRI | Move Doubleword as Direct Store |
|
||||
| MOVSB_ZL | Fast Zero-Length MOVSB |
|
||||
| MPX | Intel MPX (Memory Protection Extensions) |
|
||||
| MOVU | MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD |
|
||||
| MSRIRC | Instruction Retired Counter MSR available |
|
||||
| MSRLIST | Read/Write List of Model Specific Registers |
|
||||
| MSR_PAGEFLUSH | Page Flush MSR available |
|
||||
| NRIPS | Indicates support for NRIP save on VMEXIT |
|
||||
| NX | NX (No-Execute) bit |
|
||||
| OSXSAVE | XSAVE enabled by OS |
|
||||
| PCONFIG | PCONFIG for Intel Multi-Key Total Memory Encryption |
|
||||
| POPCNT | POPCNT instruction |
|
||||
| PPIN | AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled |
|
||||
| PREFETCHI | PREFETCHIT0/1 instructions |
|
||||
| PSFD | Predictive Store Forward Disable |
|
||||
| RDPRU | RDPRU instruction supported |
|
||||
| RDRAND | RDRAND instruction is available |
|
||||
| RDSEED | RDSEED instruction is available |
|
||||
| RDTSCP | RDTSCP Instruction |
|
||||
| RRSBA_CTRL | Restricted RSB Alternate |
|
||||
| RTM | Restricted Transactional Memory |
|
||||
| RTM_ALWAYS_ABORT | Indicates that the loaded microcode is forcing RTM abort. |
|
||||
| SERIALIZE | Serialize Instruction Execution |
|
||||
| SEV | AMD Secure Encrypted Virtualization supported |
|
||||
| SEV_64BIT | AMD SEV guest execution only allowed from a 64-bit host |
|
||||
| SEV_ALTERNATIVE | AMD SEV Alternate Injection supported |
|
||||
| SEV_DEBUGSWAP | Full debug state swap supported for SEV-ES guests |
|
||||
| SEV_ES | AMD SEV Encrypted State supported |
|
||||
| SEV_RESTRICTED | AMD SEV Restricted Injection supported |
|
||||
| SEV_SNP | AMD SEV Secure Nested Paging supported |
|
||||
| SGX | Software Guard Extensions |
|
||||
| SGXLC | Software Guard Extensions Launch Control |
|
||||
| SGXPQC | Software Guard Extensions 256-bit Encryption |
|
||||
| SHA | Intel SHA Extensions |
|
||||
| SME | AMD Secure Memory Encryption supported |
|
||||
| SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced |
|
||||
| SM3_X86 | SM3 instructions |
|
||||
| SM4_X86 | SM4 instructions |
|
||||
| SPEC_CTRL_SSBD | Speculative Store Bypass Disable |
|
||||
| SRBDS_CTRL | SRBDS mitigation MSR available |
|
||||
| SSE | SSE functions |
|
||||
| SSE2 | P4 SSE functions |
|
||||
| SSE3 | Prescott SSE3 functions |
|
||||
| SSE4 | Penryn SSE4.1 functions |
|
||||
| SSE42 | Nehalem SSE4.2 functions |
|
||||
| SSE4A | AMD Barcelona microarchitecture SSE4a instructions |
|
||||
| SSSE3 | Conroe SSSE3 functions |
|
||||
| STIBP | Single Thread Indirect Branch Predictors |
|
||||
| STIBP_ALWAYSON | AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On |
|
||||
| STOSB_SHORT | Fast short STOSB |
|
||||
| SUCCOR | Software uncorrectable error containment and recovery capability. |
|
||||
| SVM | AMD Secure Virtual Machine |
|
||||
| SVMDA | Indicates support for the SVM decode assists. |
|
||||
| SVMFBASID | SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control |
|
||||
| SVML | AMD SVM lock. Indicates support for SVM-Lock. |
|
||||
| SVMNP | AMD SVM nested paging |
|
||||
| SVMPF | SVM pause intercept filter. Indicates support for the pause intercept filter |
|
||||
| SVMPFT | SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold |
|
||||
| SYSCALL | System-Call Extension (SCE): SYSCALL and SYSRET instructions. |
|
||||
| SYSEE | SYSENTER and SYSEXIT instructions |
|
||||
| TBM | AMD Trailing Bit Manipulation |
|
||||
| TDX_GUEST | Intel Trust Domain Extensions Guest |
|
||||
| TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations |
|
||||
| TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. |
|
||||
| TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. |
|
||||
| TSA_L1_NO | AMD only: Not vulnerable to TSA-L1 |
|
||||
| TSA_SQ_NO | AMD only: Not vulnerable to TSA-SQ |
|
||||
| TSA_VERW_CLEAR | AMD: If set, the memory form of the VERW instruction may be used to help mitigate TSA |
|
||||
| TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 |
|
||||
| TSXLDTRK | Intel TSX Suspend Load Address Tracking |
|
||||
| VAES | Vector AES. AVX(512) versions requires additional checks. |
|
||||
| VMCBCLEAN | VMCB clean bits. Indicates support for VMCB clean bits. |
|
||||
| VMPL | AMD VM Permission Levels supported |
|
||||
| VMSA_REGPROT | AMD VMSA Register Protection supported |
|
||||
| VMX | Virtual Machine Extensions |
|
||||
| VPCLMULQDQ | Carry-Less Multiplication Quadword. Requires AVX for 3 register versions. |
|
||||
| VTE | AMD Virtual Transparent Encryption supported |
|
||||
| WAITPKG | TPAUSE, UMONITOR, UMWAIT |
|
||||
| WBNOINVD | Write Back and Do Not Invalidate Cache |
|
||||
| WRMSRNS | Non-Serializing Write to Model Specific Register |
|
||||
| X87 | FPU |
|
||||
| XGETBV1 | Supports XGETBV with ECX = 1 |
|
||||
| XOP | Bulldozer XOP functions |
|
||||
| XSAVE | XSAVE, XRESTOR, XSETBV, XGETBV |
|
||||
| XSAVEC | Supports XSAVEC and the compacted form of XRSTOR. |
|
||||
| XSAVEOPT | XSAVEOPT available |
|
||||
| XSAVES | Supports XSAVES/XRSTORS and IA32_XSS |
|
||||
|
||||
# ARM features:
|
||||
|
||||
| Feature Flag | Description |
|
||||
|--------------|------------------------------------------------------------------|
|
||||
| AESARM | AES instructions |
|
||||
| ARMCPUID | Some CPU ID registers readable at user-level |
|
||||
| ASIMD | Advanced SIMD |
|
||||
| ASIMDDP | SIMD Dot Product |
|
||||
| ASIMDHP | Advanced SIMD half-precision floating point |
|
||||
| ASIMDRDM | Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH) |
|
||||
| ATOMICS | Large System Extensions (LSE) |
|
||||
| CRC32 | CRC32/CRC32C instructions |
|
||||
| DCPOP | Data cache clean to Point of Persistence (DC CVAP) |
|
||||
| EVTSTRM | Generic timer |
|
||||
| FCMA | Floatin point complex number addition and multiplication |
|
||||
| FHM | FMLAL and FMLSL instructions |
|
||||
| FP | Single-precision and double-precision floating point |
|
||||
| FPHP | Half-precision floating point |
|
||||
| GPA | Generic Pointer Authentication |
|
||||
| JSCVT | Javascript-style double->int convert (FJCVTZS) |
|
||||
| LRCPC | Weaker release consistency (LDAPR, etc) |
|
||||
| PMULL | Polynomial Multiply instructions (PMULL/PMULL2) |
|
||||
| RNDR | Random Number instructions |
|
||||
| TLB | Outer Shareable and TLB range maintenance instructions |
|
||||
| TS | Flag manipulation instructions |
|
||||
| SHA1 | SHA-1 instructions (SHA1C, etc) |
|
||||
| SHA2 | SHA-2 instructions (SHA256H, etc) |
|
||||
| SHA3 | SHA-3 instructions (EOR3, RAXI, XAR, BCAX) |
|
||||
| SHA512 | SHA512 instructions |
|
||||
| SM3 | SM3 instructions |
|
||||
| SM4 | SM4 instructions |
|
||||
| SVE | Scalable Vector Extension |
|
||||
|
||||
# license
|
||||
|
||||
This code is published under an MIT license. See LICENSE file for more information.
|
||||
+1679
File diff suppressed because it is too large
Load Diff
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build 386,!gccgo,!noasm,!appengine
|
||||
|
||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuid(SB), 7, $0
|
||||
XORL CX, CX
|
||||
MOVL op+0(FP), AX
|
||||
CPUID
|
||||
MOVL AX, eax+4(FP)
|
||||
MOVL BX, ebx+8(FP)
|
||||
MOVL CX, ecx+12(FP)
|
||||
MOVL DX, edx+16(FP)
|
||||
RET
|
||||
|
||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuidex(SB), 7, $0
|
||||
MOVL op+0(FP), AX
|
||||
MOVL op2+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func xgetbv(index uint32) (eax, edx uint32)
|
||||
TEXT ·asmXgetbv(SB), 7, $0
|
||||
MOVL index+0(FP), CX
|
||||
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
|
||||
MOVL AX, eax+4(FP)
|
||||
MOVL DX, edx+8(FP)
|
||||
RET
|
||||
|
||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmRdtscpAsm(SB), 7, $0
|
||||
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
|
||||
MOVL AX, eax+0(FP)
|
||||
MOVL BX, ebx+4(FP)
|
||||
MOVL CX, ecx+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// func asmDarwinHasAVX512() bool
|
||||
TEXT ·asmDarwinHasAVX512(SB), 7, $0
|
||||
MOVL $0, eax+0(FP)
|
||||
RET
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build amd64,!gccgo,!noasm,!appengine
|
||||
|
||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuid(SB), 7, $0
|
||||
XORQ CX, CX
|
||||
MOVL op+0(FP), AX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmCpuidex(SB), 7, $0
|
||||
MOVL op+0(FP), AX
|
||||
MOVL op2+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func asmXgetbv(index uint32) (eax, edx uint32)
|
||||
TEXT ·asmXgetbv(SB), 7, $0
|
||||
MOVL index+0(FP), CX
|
||||
BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·asmRdtscpAsm(SB), 7, $0
|
||||
BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
|
||||
MOVL AX, eax+0(FP)
|
||||
MOVL BX, ebx+4(FP)
|
||||
MOVL CX, ecx+8(FP)
|
||||
MOVL DX, edx+12(FP)
|
||||
RET
|
||||
|
||||
// From https://go-review.googlesource.com/c/sys/+/285572/
|
||||
// func asmDarwinHasAVX512() bool
|
||||
TEXT ·asmDarwinHasAVX512(SB), 7, $0-1
|
||||
MOVB $0, ret+0(FP) // default to false
|
||||
|
||||
#ifdef GOOS_darwin // return if not darwin
|
||||
#ifdef GOARCH_amd64 // return if not amd64
|
||||
// These values from:
|
||||
// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
|
||||
#define commpage64_base_address 0x00007fffffe00000
|
||||
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
|
||||
#define commpage64_version (commpage64_base_address+0x01E)
|
||||
#define hasAVX512F 0x0000004000000000
|
||||
MOVQ $commpage64_version, BX
|
||||
MOVW (BX), AX
|
||||
CMPW AX, $13 // versions < 13 do not support AVX512
|
||||
JL no_avx512
|
||||
MOVQ $commpage64_cpu_capabilities64, BX
|
||||
MOVQ (BX), AX
|
||||
MOVQ $hasAVX512F, CX
|
||||
ANDQ CX, AX
|
||||
JZ no_avx512
|
||||
MOVB $1, ret+0(FP)
|
||||
|
||||
no_avx512:
|
||||
#endif
|
||||
#endif
|
||||
RET
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//+build arm64,!gccgo,!noasm,!appengine
|
||||
|
||||
// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
|
||||
|
||||
// func getMidr
|
||||
TEXT ·getMidr(SB), 7, $0
|
||||
WORD $0xd5380000 // mrs x0, midr_el1 /* Main ID Register */
|
||||
MOVD R0, midr+0(FP)
|
||||
RET
|
||||
|
||||
// func getProcFeatures
|
||||
TEXT ·getProcFeatures(SB), 7, $0
|
||||
WORD $0xd5380400 // mrs x0, id_aa64pfr0_el1 /* Processor Feature Register 0 */
|
||||
MOVD R0, procFeatures+0(FP)
|
||||
RET
|
||||
|
||||
// func getInstAttributes
|
||||
TEXT ·getInstAttributes(SB), 7, $0
|
||||
WORD $0xd5380600 // mrs x0, id_aa64isar0_el1 /* Instruction Set Attribute Register 0 */
|
||||
WORD $0xd5380621 // mrs x1, id_aa64isar1_el1 /* Instruction Set Attribute Register 1 */
|
||||
MOVD R0, instAttrReg0+0(FP)
|
||||
MOVD R1, instAttrReg1+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·getVectorLength(SB), 7, $0
|
||||
WORD $0xd2800002 // mov x2, #0
|
||||
WORD $0x04225022 // addvl x2, x2, #1
|
||||
WORD $0xd37df042 // lsl x2, x2, #3
|
||||
WORD $0xd2800003 // mov x3, #0
|
||||
WORD $0x04635023 // addpl x3, x3, #1
|
||||
WORD $0xd37df063 // lsl x3, x3, #3
|
||||
MOVD R2, vl+0(FP)
|
||||
MOVD R3, pl+8(FP)
|
||||
RET
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build arm64 && !gccgo && !noasm && !appengine
|
||||
// +build arm64,!gccgo,!noasm,!appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
import "runtime"
|
||||
|
||||
func getMidr() (midr uint64)
|
||||
func getProcFeatures() (procFeatures uint64)
|
||||
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
|
||||
func getVectorLength() (vl, pl uint64)
|
||||
|
||||
func initCPU() {
|
||||
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
|
||||
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
}
|
||||
|
||||
func addInfo(c *CPUInfo, safe bool) {
|
||||
// Seems to be safe to assume on ARM64
|
||||
c.CacheLine = 64
|
||||
detectOS(c)
|
||||
|
||||
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
|
||||
if safe && !c.Has(ARMCPUID) && runtime.GOOS != "freebsd" {
|
||||
return
|
||||
}
|
||||
midr := getMidr()
|
||||
|
||||
// MIDR_EL1 - Main ID Register
|
||||
// https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/midr_el1
|
||||
// x--------------------------------------------------x
|
||||
// | Name | bits | visible |
|
||||
// |--------------------------------------------------|
|
||||
// | Implementer | [31-24] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | Variant | [23-20] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | Architecture | [19-16] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | PartNum | [15-4] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | Revision | [3-0] | y |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
switch (midr >> 24) & 0xff {
|
||||
case 0xC0:
|
||||
c.VendorString = "Ampere Computing"
|
||||
c.VendorID = Ampere
|
||||
case 0x41:
|
||||
c.VendorString = "Arm Limited"
|
||||
c.VendorID = ARM
|
||||
case 0x42:
|
||||
c.VendorString = "Broadcom Corporation"
|
||||
c.VendorID = Broadcom
|
||||
case 0x43:
|
||||
c.VendorString = "Cavium Inc"
|
||||
c.VendorID = Cavium
|
||||
case 0x44:
|
||||
c.VendorString = "Digital Equipment Corporation"
|
||||
c.VendorID = DEC
|
||||
case 0x46:
|
||||
c.VendorString = "Fujitsu Ltd"
|
||||
c.VendorID = Fujitsu
|
||||
case 0x49:
|
||||
c.VendorString = "Infineon Technologies AG"
|
||||
c.VendorID = Infineon
|
||||
case 0x4D:
|
||||
c.VendorString = "Motorola or Freescale Semiconductor Inc"
|
||||
c.VendorID = Motorola
|
||||
case 0x4E:
|
||||
c.VendorString = "NVIDIA Corporation"
|
||||
c.VendorID = NVIDIA
|
||||
case 0x50:
|
||||
c.VendorString = "Applied Micro Circuits Corporation"
|
||||
c.VendorID = AMCC
|
||||
case 0x51:
|
||||
c.VendorString = "Qualcomm Inc"
|
||||
c.VendorID = Qualcomm
|
||||
case 0x56:
|
||||
c.VendorString = "Marvell International Ltd"
|
||||
c.VendorID = Marvell
|
||||
case 0x69:
|
||||
c.VendorString = "Intel Corporation"
|
||||
c.VendorID = Intel
|
||||
}
|
||||
|
||||
// Lower 4 bits: Architecture
|
||||
// Architecture Meaning
|
||||
// 0b0001 Armv4.
|
||||
// 0b0010 Armv4T.
|
||||
// 0b0011 Armv5 (obsolete).
|
||||
// 0b0100 Armv5T.
|
||||
// 0b0101 Armv5TE.
|
||||
// 0b0110 Armv5TEJ.
|
||||
// 0b0111 Armv6.
|
||||
// 0b1111 Architectural features are individually identified in the ID_* registers, see 'ID registers'.
|
||||
// Upper 4 bit: Variant
|
||||
// An IMPLEMENTATION DEFINED variant number.
|
||||
// Typically, this field is used to distinguish between different product variants, or major revisions of a product.
|
||||
c.Family = int(midr>>16) & 0xff
|
||||
|
||||
// PartNum, bits [15:4]
|
||||
// An IMPLEMENTATION DEFINED primary part number for the device.
|
||||
// On processors implemented by Arm, if the top four bits of the primary
|
||||
// part number are 0x0 or 0x7, the variant and architecture are encoded differently.
|
||||
// Revision, bits [3:0]
|
||||
// An IMPLEMENTATION DEFINED revision number for the device.
|
||||
c.Model = int(midr) & 0xffff
|
||||
|
||||
procFeatures := getProcFeatures()
|
||||
|
||||
// ID_AA64PFR0_EL1 - Processor Feature Register 0
|
||||
// x--------------------------------------------------x
|
||||
// | Name | bits | visible |
|
||||
// |--------------------------------------------------|
|
||||
// | DIT | [51-48] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SVE | [35-32] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | GIC | [27-24] | n |
|
||||
// |--------------------------------------------------|
|
||||
// | AdvSIMD | [23-20] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | FP | [19-16] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | EL3 | [15-12] | n |
|
||||
// |--------------------------------------------------|
|
||||
// | EL2 | [11-8] | n |
|
||||
// |--------------------------------------------------|
|
||||
// | EL1 | [7-4] | n |
|
||||
// |--------------------------------------------------|
|
||||
// | EL0 | [3-0] | n |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
var f flagSet
|
||||
// if procFeatures&(0xf<<48) != 0 {
|
||||
// fmt.Println("DIT")
|
||||
// }
|
||||
f.setIf(procFeatures&(0xf<<32) != 0, SVE)
|
||||
if procFeatures&(0xf<<20) != 15<<20 {
|
||||
f.set(ASIMD)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
|
||||
// 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
|
||||
f.setIf(procFeatures&(0xf<<20) == 1<<20, FPHP, ASIMDHP)
|
||||
}
|
||||
f.setIf(procFeatures&(0xf<<16) != 0, FP)
|
||||
|
||||
instAttrReg0, instAttrReg1 := getInstAttributes()
|
||||
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
//
|
||||
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
|
||||
// x--------------------------------------------------x
|
||||
// | Name | bits | visible |
|
||||
// |--------------------------------------------------|
|
||||
// | RNDR | [63-60] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | TLB | [59-56] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | TS | [55-52] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | FHM | [51-48] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | DP | [47-44] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SM4 | [43-40] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SM3 | [39-36] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SHA3 | [35-32] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | RDM | [31-28] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | ATOMICS | [23-20] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | CRC32 | [19-16] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SHA2 | [15-12] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | SHA1 | [11-8] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | AES | [7-4] | y |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
f.setIf(instAttrReg0&(0xf<<60) != 0, RNDR)
|
||||
f.setIf(instAttrReg0&(0xf<<56) != 0, TLB)
|
||||
f.setIf(instAttrReg0&(0xf<<52) != 0, TS)
|
||||
f.setIf(instAttrReg0&(0xf<<48) != 0, FHM)
|
||||
f.setIf(instAttrReg0&(0xf<<44) != 0, ASIMDDP)
|
||||
f.setIf(instAttrReg0&(0xf<<40) != 0, SM4)
|
||||
f.setIf(instAttrReg0&(0xf<<36) != 0, SM3)
|
||||
f.setIf(instAttrReg0&(0xf<<32) != 0, SHA3)
|
||||
f.setIf(instAttrReg0&(0xf<<28) != 0, ASIMDRDM)
|
||||
f.setIf(instAttrReg0&(0xf<<20) != 0, ATOMICS)
|
||||
f.setIf(instAttrReg0&(0xf<<16) != 0, CRC32)
|
||||
f.setIf(instAttrReg0&(0xf<<12) != 0, SHA2)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
|
||||
f.setIf(instAttrReg0&(0xf<<12) == 2<<12, SHA512)
|
||||
f.setIf(instAttrReg0&(0xf<<8) != 0, SHA1)
|
||||
f.setIf(instAttrReg0&(0xf<<4) != 0, AESARM)
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
|
||||
// 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
|
||||
f.setIf(instAttrReg0&(0xf<<4) == 2<<4, PMULL)
|
||||
|
||||
// https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
|
||||
//
|
||||
// ID_AA64ISAR1_EL1 - Instruction set attribute register 1
|
||||
// x--------------------------------------------------x
|
||||
// | Name | bits | visible |
|
||||
// |--------------------------------------------------|
|
||||
// | GPI | [31-28] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | GPA | [27-24] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | LRCPC | [23-20] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | FCMA | [19-16] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | JSCVT | [15-12] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | API | [11-8] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | APA | [7-4] | y |
|
||||
// |--------------------------------------------------|
|
||||
// | DPB | [3-0] | y |
|
||||
// x--------------------------------------------------x
|
||||
|
||||
// if instAttrReg1&(0xf<<28) != 0 {
|
||||
// fmt.Println("GPI")
|
||||
// }
|
||||
f.setIf(instAttrReg1&(0xf<<28) != 24, GPA)
|
||||
f.setIf(instAttrReg1&(0xf<<20) != 0, LRCPC)
|
||||
f.setIf(instAttrReg1&(0xf<<16) != 0, FCMA)
|
||||
f.setIf(instAttrReg1&(0xf<<12) != 0, JSCVT)
|
||||
// if instAttrReg1&(0xf<<8) != 0 {
|
||||
// fmt.Println("API")
|
||||
// }
|
||||
// if instAttrReg1&(0xf<<4) != 0 {
|
||||
// fmt.Println("APA")
|
||||
// }
|
||||
f.setIf(instAttrReg1&(0xf<<0) != 0, DCPOP)
|
||||
|
||||
// Store
|
||||
c.featureSet.or(f)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !386 && !arm64) || gccgo || noasm || appengine
|
||||
// +build !amd64,!386,!arm64 gccgo noasm appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
func initCPU() {
|
||||
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
|
||||
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
|
||||
|
||||
}
|
||||
|
||||
func addInfo(info *CPUInfo, safe bool) {}
|
||||
func getVectorLength() (vl, pl uint64) { return 0, 0 }
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build (386 && !gccgo && !noasm && !appengine) || (amd64 && !gccgo && !noasm && !appengine)
|
||||
// +build 386,!gccgo,!noasm,!appengine amd64,!gccgo,!noasm,!appengine
|
||||
|
||||
package cpuid
|
||||
|
||||
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
func asmXgetbv(index uint32) (eax, edx uint32)
|
||||
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
|
||||
func asmDarwinHasAVX512() bool
|
||||
|
||||
func initCPU() {
|
||||
cpuid = asmCpuid
|
||||
cpuidex = asmCpuidex
|
||||
xgetbv = asmXgetbv
|
||||
rdtscpAsm = asmRdtscpAsm
|
||||
darwinHasAVX512 = asmDarwinHasAVX512
|
||||
}
|
||||
|
||||
func addInfo(c *CPUInfo, safe bool) {
|
||||
c.maxFunc = maxFunctionID()
|
||||
c.maxExFunc = maxExtendedFunction()
|
||||
c.BrandName = brandName()
|
||||
c.CacheLine = cacheLine()
|
||||
c.Family, c.Model, c.Stepping = familyModel()
|
||||
c.featureSet = support()
|
||||
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
|
||||
c.AMDMemEncryption = hasAMDMemEncryption(c.featureSet.inSet(SME) || c.featureSet.inSet(SEV))
|
||||
c.ThreadsPerCore = threadsPerCore()
|
||||
c.LogicalCores = logicalCores()
|
||||
c.PhysicalCores = physicalCores()
|
||||
c.VendorID, c.VendorString = vendorID()
|
||||
c.HypervisorVendorID, c.HypervisorVendorString = hypervisorVendorID()
|
||||
c.AVX10Level = c.supportAVX10()
|
||||
c.cacheSize()
|
||||
c.frequencies()
|
||||
if c.maxFunc >= 0x0A {
|
||||
eax, ebx, _, edx := cpuid(0x0A)
|
||||
c.PMU = parseLeaf0AH(c, eax, ebx, edx)
|
||||
}
|
||||
}
|
||||
|
||||
func getVectorLength() (vl, pl uint64) { return 0, 0 }
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
// Code generated by "stringer -type=FeatureID,Vendor"; DO NOT EDIT.
|
||||
|
||||
package cpuid
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ADX-1]
|
||||
_ = x[AESNI-2]
|
||||
_ = x[AMD3DNOW-3]
|
||||
_ = x[AMD3DNOWEXT-4]
|
||||
_ = x[AMXBF16-5]
|
||||
_ = x[AMXFP16-6]
|
||||
_ = x[AMXINT8-7]
|
||||
_ = x[AMXFP8-8]
|
||||
_ = x[AMXTILE-9]
|
||||
_ = x[AMXTF32-10]
|
||||
_ = x[AMXCOMPLEX-11]
|
||||
_ = x[AMXTRANSPOSE-12]
|
||||
_ = x[APX_F-13]
|
||||
_ = x[AVX-14]
|
||||
_ = x[AVX10-15]
|
||||
_ = x[AVX10_128-16]
|
||||
_ = x[AVX10_256-17]
|
||||
_ = x[AVX10_512-18]
|
||||
_ = x[AVX2-19]
|
||||
_ = x[AVX512BF16-20]
|
||||
_ = x[AVX512BITALG-21]
|
||||
_ = x[AVX512BW-22]
|
||||
_ = x[AVX512CD-23]
|
||||
_ = x[AVX512DQ-24]
|
||||
_ = x[AVX512ER-25]
|
||||
_ = x[AVX512F-26]
|
||||
_ = x[AVX512FP16-27]
|
||||
_ = x[AVX512IFMA-28]
|
||||
_ = x[AVX512PF-29]
|
||||
_ = x[AVX512VBMI-30]
|
||||
_ = x[AVX512VBMI2-31]
|
||||
_ = x[AVX512VL-32]
|
||||
_ = x[AVX512VNNI-33]
|
||||
_ = x[AVX512VP2INTERSECT-34]
|
||||
_ = x[AVX512VPOPCNTDQ-35]
|
||||
_ = x[AVXIFMA-36]
|
||||
_ = x[AVXNECONVERT-37]
|
||||
_ = x[AVXSLOW-38]
|
||||
_ = x[AVXVNNI-39]
|
||||
_ = x[AVXVNNIINT8-40]
|
||||
_ = x[AVXVNNIINT16-41]
|
||||
_ = x[BHI_CTRL-42]
|
||||
_ = x[BMI1-43]
|
||||
_ = x[BMI2-44]
|
||||
_ = x[CETIBT-45]
|
||||
_ = x[CETSS-46]
|
||||
_ = x[CLDEMOTE-47]
|
||||
_ = x[CLMUL-48]
|
||||
_ = x[CLZERO-49]
|
||||
_ = x[CMOV-50]
|
||||
_ = x[CMPCCXADD-51]
|
||||
_ = x[CMPSB_SCADBS_SHORT-52]
|
||||
_ = x[CMPXCHG8-53]
|
||||
_ = x[CPBOOST-54]
|
||||
_ = x[CPPC-55]
|
||||
_ = x[CX16-56]
|
||||
_ = x[EFER_LMSLE_UNS-57]
|
||||
_ = x[ENQCMD-58]
|
||||
_ = x[ERMS-59]
|
||||
_ = x[F16C-60]
|
||||
_ = x[FLUSH_L1D-61]
|
||||
_ = x[FMA3-62]
|
||||
_ = x[FMA4-63]
|
||||
_ = x[FP128-64]
|
||||
_ = x[FP256-65]
|
||||
_ = x[FSRM-66]
|
||||
_ = x[FXSR-67]
|
||||
_ = x[FXSROPT-68]
|
||||
_ = x[GFNI-69]
|
||||
_ = x[HLE-70]
|
||||
_ = x[HRESET-71]
|
||||
_ = x[HTT-72]
|
||||
_ = x[HWA-73]
|
||||
_ = x[HYBRID_CPU-74]
|
||||
_ = x[HYPERVISOR-75]
|
||||
_ = x[IA32_ARCH_CAP-76]
|
||||
_ = x[IA32_CORE_CAP-77]
|
||||
_ = x[IBPB-78]
|
||||
_ = x[IBPB_BRTYPE-79]
|
||||
_ = x[IBRS-80]
|
||||
_ = x[IBRS_PREFERRED-81]
|
||||
_ = x[IBRS_PROVIDES_SMP-82]
|
||||
_ = x[IBS-83]
|
||||
_ = x[IBSBRNTRGT-84]
|
||||
_ = x[IBSFETCHSAM-85]
|
||||
_ = x[IBSFFV-86]
|
||||
_ = x[IBSOPCNT-87]
|
||||
_ = x[IBSOPCNTEXT-88]
|
||||
_ = x[IBSOPSAM-89]
|
||||
_ = x[IBSRDWROPCNT-90]
|
||||
_ = x[IBSRIPINVALIDCHK-91]
|
||||
_ = x[IBS_FETCH_CTLX-92]
|
||||
_ = x[IBS_OPDATA4-93]
|
||||
_ = x[IBS_OPFUSE-94]
|
||||
_ = x[IBS_PREVENTHOST-95]
|
||||
_ = x[IBS_ZEN4-96]
|
||||
_ = x[IDPRED_CTRL-97]
|
||||
_ = x[INT_WBINVD-98]
|
||||
_ = x[INVLPGB-99]
|
||||
_ = x[KEYLOCKER-100]
|
||||
_ = x[KEYLOCKERW-101]
|
||||
_ = x[LAHF-102]
|
||||
_ = x[LAM-103]
|
||||
_ = x[LBRVIRT-104]
|
||||
_ = x[LZCNT-105]
|
||||
_ = x[MCAOVERFLOW-106]
|
||||
_ = x[MCDT_NO-107]
|
||||
_ = x[MCOMMIT-108]
|
||||
_ = x[MD_CLEAR-109]
|
||||
_ = x[MMX-110]
|
||||
_ = x[MMXEXT-111]
|
||||
_ = x[MOVBE-112]
|
||||
_ = x[MOVDIR64B-113]
|
||||
_ = x[MOVDIRI-114]
|
||||
_ = x[MOVSB_ZL-115]
|
||||
_ = x[MOVU-116]
|
||||
_ = x[MPX-117]
|
||||
_ = x[MSRIRC-118]
|
||||
_ = x[MSRLIST-119]
|
||||
_ = x[MSR_PAGEFLUSH-120]
|
||||
_ = x[NRIPS-121]
|
||||
_ = x[NX-122]
|
||||
_ = x[OSXSAVE-123]
|
||||
_ = x[PCONFIG-124]
|
||||
_ = x[POPCNT-125]
|
||||
_ = x[PPIN-126]
|
||||
_ = x[PREFETCHI-127]
|
||||
_ = x[PSFD-128]
|
||||
_ = x[RDPRU-129]
|
||||
_ = x[RDRAND-130]
|
||||
_ = x[RDSEED-131]
|
||||
_ = x[RDTSCP-132]
|
||||
_ = x[RRSBA_CTRL-133]
|
||||
_ = x[RTM-134]
|
||||
_ = x[RTM_ALWAYS_ABORT-135]
|
||||
_ = x[SBPB-136]
|
||||
_ = x[SERIALIZE-137]
|
||||
_ = x[SEV-138]
|
||||
_ = x[SEV_64BIT-139]
|
||||
_ = x[SEV_ALTERNATIVE-140]
|
||||
_ = x[SEV_DEBUGSWAP-141]
|
||||
_ = x[SEV_ES-142]
|
||||
_ = x[SEV_RESTRICTED-143]
|
||||
_ = x[SEV_SNP-144]
|
||||
_ = x[SGX-145]
|
||||
_ = x[SGXLC-146]
|
||||
_ = x[SGXPQC-147]
|
||||
_ = x[SHA-148]
|
||||
_ = x[SME-149]
|
||||
_ = x[SME_COHERENT-150]
|
||||
_ = x[SM3_X86-151]
|
||||
_ = x[SM4_X86-152]
|
||||
_ = x[SPEC_CTRL_SSBD-153]
|
||||
_ = x[SRBDS_CTRL-154]
|
||||
_ = x[SRSO_MSR_FIX-155]
|
||||
_ = x[SRSO_NO-156]
|
||||
_ = x[SRSO_USER_KERNEL_NO-157]
|
||||
_ = x[SSE-158]
|
||||
_ = x[SSE2-159]
|
||||
_ = x[SSE3-160]
|
||||
_ = x[SSE4-161]
|
||||
_ = x[SSE42-162]
|
||||
_ = x[SSE4A-163]
|
||||
_ = x[SSSE3-164]
|
||||
_ = x[STIBP-165]
|
||||
_ = x[STIBP_ALWAYSON-166]
|
||||
_ = x[STOSB_SHORT-167]
|
||||
_ = x[SUCCOR-168]
|
||||
_ = x[SVM-169]
|
||||
_ = x[SVMDA-170]
|
||||
_ = x[SVMFBASID-171]
|
||||
_ = x[SVML-172]
|
||||
_ = x[SVMNP-173]
|
||||
_ = x[SVMPF-174]
|
||||
_ = x[SVMPFT-175]
|
||||
_ = x[SYSCALL-176]
|
||||
_ = x[SYSEE-177]
|
||||
_ = x[TBM-178]
|
||||
_ = x[TDX_GUEST-179]
|
||||
_ = x[TLB_FLUSH_NESTED-180]
|
||||
_ = x[TME-181]
|
||||
_ = x[TOPEXT-182]
|
||||
_ = x[TSA_L1_NO-183]
|
||||
_ = x[TSA_SQ_NO-184]
|
||||
_ = x[TSA_VERW_CLEAR-185]
|
||||
_ = x[TSCRATEMSR-186]
|
||||
_ = x[TSXLDTRK-187]
|
||||
_ = x[VAES-188]
|
||||
_ = x[VMCBCLEAN-189]
|
||||
_ = x[VMPL-190]
|
||||
_ = x[VMSA_REGPROT-191]
|
||||
_ = x[VMX-192]
|
||||
_ = x[VPCLMULQDQ-193]
|
||||
_ = x[VTE-194]
|
||||
_ = x[WAITPKG-195]
|
||||
_ = x[WBNOINVD-196]
|
||||
_ = x[WRMSRNS-197]
|
||||
_ = x[X87-198]
|
||||
_ = x[XGETBV1-199]
|
||||
_ = x[XOP-200]
|
||||
_ = x[XSAVE-201]
|
||||
_ = x[XSAVEC-202]
|
||||
_ = x[XSAVEOPT-203]
|
||||
_ = x[XSAVES-204]
|
||||
_ = x[AESARM-205]
|
||||
_ = x[ARMCPUID-206]
|
||||
_ = x[ASIMD-207]
|
||||
_ = x[ASIMDDP-208]
|
||||
_ = x[ASIMDHP-209]
|
||||
_ = x[ASIMDRDM-210]
|
||||
_ = x[ATOMICS-211]
|
||||
_ = x[CRC32-212]
|
||||
_ = x[DCPOP-213]
|
||||
_ = x[EVTSTRM-214]
|
||||
_ = x[FCMA-215]
|
||||
_ = x[FHM-216]
|
||||
_ = x[FP-217]
|
||||
_ = x[FPHP-218]
|
||||
_ = x[GPA-219]
|
||||
_ = x[JSCVT-220]
|
||||
_ = x[LRCPC-221]
|
||||
_ = x[PMULL-222]
|
||||
_ = x[RNDR-223]
|
||||
_ = x[TLB-224]
|
||||
_ = x[TS-225]
|
||||
_ = x[SHA1-226]
|
||||
_ = x[SHA2-227]
|
||||
_ = x[SHA3-228]
|
||||
_ = x[SHA512-229]
|
||||
_ = x[SM3-230]
|
||||
_ = x[SM4-231]
|
||||
_ = x[SVE-232]
|
||||
_ = x[PMU_FIXEDCOUNTER_CYCLES-233]
|
||||
_ = x[PMU_FIXEDCOUNTER_REFCYCLES-234]
|
||||
_ = x[PMU_FIXEDCOUNTER_INSTRUCTIONS-235]
|
||||
_ = x[PMU_FIXEDCOUNTER_TOPDOWN_SLOTS-236]
|
||||
_ = x[lastID-237]
|
||||
_ = x[firstID-0]
|
||||
}
|
||||
|
||||
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAMXTF32AMXCOMPLEXAMXTRANSPOSEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSGXPQCSHASMESME_COHERENTSM3_X86SM4_X86SPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSA_L1_NOTSA_SQ_NOTSA_VERW_CLEARTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFHMFPFPHPGPAJSCVTLRCPCPMULLRNDRTLBTSSHA1SHA2SHA3SHA512SM3SM4SVEPMU_FIXEDCOUNTER_CYCLESPMU_FIXEDCOUNTER_REFCYCLESPMU_FIXEDCOUNTER_INSTRUCTIONSPMU_FIXEDCOUNTER_TOPDOWN_SLOTSlastID"
|
||||
|
||||
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 75, 85, 97, 102, 105, 110, 119, 128, 137, 141, 151, 163, 171, 179, 187, 195, 202, 212, 222, 230, 240, 251, 259, 269, 287, 302, 309, 321, 328, 335, 346, 358, 366, 370, 374, 380, 385, 393, 398, 404, 408, 417, 435, 443, 450, 454, 458, 472, 478, 482, 486, 495, 499, 503, 508, 513, 517, 521, 528, 532, 535, 541, 544, 547, 557, 567, 580, 593, 597, 608, 612, 626, 643, 646, 656, 667, 673, 681, 692, 700, 712, 728, 742, 753, 763, 778, 786, 797, 807, 814, 823, 833, 837, 840, 847, 852, 863, 870, 877, 885, 888, 894, 899, 908, 915, 923, 927, 930, 936, 943, 956, 961, 963, 970, 977, 983, 987, 996, 1000, 1005, 1011, 1017, 1023, 1033, 1036, 1052, 1056, 1065, 1068, 1077, 1092, 1105, 1111, 1125, 1132, 1135, 1140, 1146, 1149, 1152, 1164, 1171, 1178, 1192, 1202, 1214, 1221, 1240, 1243, 1247, 1251, 1255, 1260, 1265, 1270, 1275, 1289, 1300, 1306, 1309, 1314, 1323, 1327, 1332, 1337, 1343, 1350, 1355, 1358, 1367, 1383, 1386, 1392, 1401, 1410, 1424, 1434, 1442, 1446, 1455, 1459, 1471, 1474, 1484, 1487, 1494, 1502, 1509, 1512, 1519, 1522, 1527, 1533, 1541, 1547, 1553, 1561, 1566, 1573, 1580, 1588, 1595, 1600, 1605, 1612, 1616, 1619, 1621, 1625, 1628, 1633, 1638, 1643, 1647, 1650, 1652, 1656, 1660, 1664, 1670, 1673, 1676, 1679, 1702, 1728, 1757, 1787, 1793}
|
||||
|
||||
func (i FeatureID) String() string {
|
||||
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
|
||||
return "FeatureID(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FeatureID_name[_FeatureID_index[i]:_FeatureID_index[i+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[VendorUnknown-0]
|
||||
_ = x[Intel-1]
|
||||
_ = x[AMD-2]
|
||||
_ = x[VIA-3]
|
||||
_ = x[Transmeta-4]
|
||||
_ = x[NSC-5]
|
||||
_ = x[KVM-6]
|
||||
_ = x[MSVM-7]
|
||||
_ = x[VMware-8]
|
||||
_ = x[XenHVM-9]
|
||||
_ = x[Bhyve-10]
|
||||
_ = x[Hygon-11]
|
||||
_ = x[SiS-12]
|
||||
_ = x[RDC-13]
|
||||
_ = x[Ampere-14]
|
||||
_ = x[ARM-15]
|
||||
_ = x[Broadcom-16]
|
||||
_ = x[Cavium-17]
|
||||
_ = x[DEC-18]
|
||||
_ = x[Fujitsu-19]
|
||||
_ = x[Infineon-20]
|
||||
_ = x[Motorola-21]
|
||||
_ = x[NVIDIA-22]
|
||||
_ = x[AMCC-23]
|
||||
_ = x[Qualcomm-24]
|
||||
_ = x[Marvell-25]
|
||||
_ = x[QEMU-26]
|
||||
_ = x[QNX-27]
|
||||
_ = x[ACRN-28]
|
||||
_ = x[SRE-29]
|
||||
_ = x[Apple-30]
|
||||
_ = x[lastVendor-31]
|
||||
}
|
||||
|
||||
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvellQEMUQNXACRNSREApplelastVendor"
|
||||
|
||||
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 149, 152, 156, 159, 164, 174}
|
||||
|
||||
func (i Vendor) String() string {
|
||||
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {
|
||||
return "Vendor(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Vendor_name[_Vendor_index[i]:_Vendor_index[i+1]]
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
if runtime.GOOS != "ios" {
|
||||
tryToFillCPUInfoFomSysctl(c)
|
||||
}
|
||||
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
|
||||
// to detect their supported state dynamically. Assume the CPU features that
|
||||
// Apple Silicon M1 supports to be available as a minimal set of features
|
||||
// to all Go programs running on darwin/arm64.
|
||||
// TODO: Add more if we know them.
|
||||
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func sysctlGetBool(name string) bool {
|
||||
value, err := unix.SysctlUint32(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return value != 0
|
||||
}
|
||||
|
||||
func sysctlGetString(name string) string {
|
||||
value, err := unix.Sysctl(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func sysctlGetInt(unknown int, names ...string) int {
|
||||
for _, name := range names {
|
||||
value, err := unix.SysctlUint32(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if value != 0 {
|
||||
return int(value)
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func sysctlGetInt64(unknown int, names ...string) int {
|
||||
for _, name := range names {
|
||||
value64, err := unix.SysctlUint64(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if int(value64) != unknown {
|
||||
return int(value64)
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
func setFeature(c *CPUInfo, feature FeatureID, aliases ...string) {
|
||||
for _, alias := range aliases {
|
||||
set := sysctlGetBool(alias)
|
||||
c.featureSet.setIf(set, feature)
|
||||
if set {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
|
||||
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
|
||||
|
||||
if len(c.BrandName) != 0 {
|
||||
c.VendorString = strings.Fields(c.BrandName)[0]
|
||||
}
|
||||
|
||||
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
|
||||
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
|
||||
sysctlGetInt(1, "hw.physicalcpu")
|
||||
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
|
||||
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
|
||||
c.Model = sysctlGetInt(0, "machdep.cpu.model")
|
||||
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
|
||||
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
|
||||
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
|
||||
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
|
||||
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
|
||||
|
||||
// ARM features:
|
||||
//
|
||||
// Note: On some Apple Silicon system, some feats have aliases. See:
|
||||
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
|
||||
// When so, we look at all aliases and consider a feature available when at least one identifier matches.
|
||||
setFeature(c, AESARM, "hw.optional.arm.FEAT_AES") // AES instructions
|
||||
setFeature(c, ASIMD, "hw.optional.arm.AdvSIMD", "hw.optional.neon") // Advanced SIMD
|
||||
setFeature(c, ASIMDDP, "hw.optional.arm.FEAT_DotProd") // SIMD Dot Product
|
||||
setFeature(c, ASIMDHP, "hw.optional.arm.AdvSIMD_HPFPCvt", "hw.optional.neon_hpfp") // Advanced SIMD half-precision floating point
|
||||
setFeature(c, ASIMDRDM, "hw.optional.arm.FEAT_RDM") // Rounding Double Multiply Accumulate/Subtract
|
||||
setFeature(c, ATOMICS, "hw.optional.arm.FEAT_LSE", "hw.optional.armv8_1_atomics") // Large System Extensions (LSE)
|
||||
setFeature(c, CRC32, "hw.optional.arm.FEAT_CRC32", "hw.optional.armv8_crc32") // CRC32/CRC32C instructions
|
||||
setFeature(c, DCPOP, "hw.optional.arm.FEAT_DPB") // Data cache clean to Point of Persistence (DC CVAP)
|
||||
setFeature(c, EVTSTRM, "hw.optional.arm.FEAT_ECV") // Generic timer
|
||||
setFeature(c, FCMA, "hw.optional.arm.FEAT_FCMA", "hw.optional.armv8_3_compnum") // Floating point complex number addition and multiplication
|
||||
setFeature(c, FHM, "hw.optional.armv8_2_fhm", "hw.optional.arm.FEAT_FHM") // FMLAL and FMLSL instructions
|
||||
setFeature(c, FP, "hw.optional.floatingpoint") // Single-precision and double-precision floating point
|
||||
setFeature(c, FPHP, "hw.optional.arm.FEAT_FP16", "hw.optional.neon_fp16") // Half-precision floating point
|
||||
setFeature(c, GPA, "hw.optional.arm.FEAT_PAuth") // Generic Pointer Authentication
|
||||
setFeature(c, JSCVT, "hw.optional.arm.FEAT_JSCVT") // Javascript-style double->int convert (FJCVTZS)
|
||||
setFeature(c, LRCPC, "hw.optional.arm.FEAT_LRCPC") // Weaker release consistency (LDAPR, etc)
|
||||
setFeature(c, PMULL, "hw.optional.arm.FEAT_PMULL") // Polynomial Multiply instructions (PMULL/PMULL2)
|
||||
setFeature(c, RNDR, "hw.optional.arm.FEAT_RNG") // Random Number instructions
|
||||
setFeature(c, TLB, "hw.optional.arm.FEAT_TLBIOS", "hw.optional.arm.FEAT_TLBIRANGE") // Outer Shareable and TLB range maintenance instructions
|
||||
setFeature(c, TS, "hw.optional.arm.FEAT_FlagM", "hw.optional.arm.FEAT_FlagM2") // Flag manipulation instructions
|
||||
setFeature(c, SHA1, "hw.optional.arm.FEAT_SHA1") // SHA-1 instructions (SHA1C, etc)
|
||||
setFeature(c, SHA2, "hw.optional.arm.FEAT_SHA256") // SHA-2 instructions (SHA256H, etc)
|
||||
setFeature(c, SHA3, "hw.optional.arm.FEAT_SHA3") // SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
|
||||
setFeature(c, SHA512, "hw.optional.arm.FEAT_SHA512") // SHA512 instructions
|
||||
setFeature(c, SM3, "hw.optional.arm.FEAT_SM3") // SM3 instructions
|
||||
setFeature(c, SM4, "hw.optional.arm.FEAT_SM4") // SM4 instructions
|
||||
setFeature(c, SVE, "hw.optional.arm.FEAT_SVE") // Scalable Vector Extension
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file located
|
||||
// here https://github.com/golang/sys/blob/master/LICENSE
|
||||
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// HWCAP bits.
|
||||
const (
|
||||
hwcap_FP = 1 << 0
|
||||
hwcap_ASIMD = 1 << 1
|
||||
hwcap_EVTSTRM = 1 << 2
|
||||
hwcap_AES = 1 << 3
|
||||
hwcap_PMULL = 1 << 4
|
||||
hwcap_SHA1 = 1 << 5
|
||||
hwcap_SHA2 = 1 << 6
|
||||
hwcap_CRC32 = 1 << 7
|
||||
hwcap_ATOMICS = 1 << 8
|
||||
hwcap_FPHP = 1 << 9
|
||||
hwcap_ASIMDHP = 1 << 10
|
||||
hwcap_CPUID = 1 << 11
|
||||
hwcap_ASIMDRDM = 1 << 12
|
||||
hwcap_JSCVT = 1 << 13
|
||||
hwcap_FCMA = 1 << 14
|
||||
hwcap_LRCPC = 1 << 15
|
||||
hwcap_DCPOP = 1 << 16
|
||||
hwcap_SHA3 = 1 << 17
|
||||
hwcap_SM3 = 1 << 18
|
||||
hwcap_SM4 = 1 << 19
|
||||
hwcap_ASIMDDP = 1 << 20
|
||||
hwcap_SHA512 = 1 << 21
|
||||
hwcap_SVE = 1 << 22
|
||||
hwcap_ASIMDFHM = 1 << 23
|
||||
hwcap_DIT = 1 << 24
|
||||
hwcap_USCAT = 1 << 25
|
||||
hwcap_ILRCPC = 1 << 26
|
||||
hwcap_FLAGM = 1 << 27
|
||||
hwcap_SSBS = 1 << 28
|
||||
hwcap_SB = 1 << 29
|
||||
hwcap_PACA = 1 << 30
|
||||
hwcap_PACG = 1 << 31
|
||||
hwcap_GCS = 1 << 32
|
||||
|
||||
hwcap2_DCPODP = 1 << 0
|
||||
hwcap2_SVE2 = 1 << 1
|
||||
hwcap2_SVEAES = 1 << 2
|
||||
hwcap2_SVEPMULL = 1 << 3
|
||||
hwcap2_SVEBITPERM = 1 << 4
|
||||
hwcap2_SVESHA3 = 1 << 5
|
||||
hwcap2_SVESM4 = 1 << 6
|
||||
hwcap2_FLAGM2 = 1 << 7
|
||||
hwcap2_FRINT = 1 << 8
|
||||
hwcap2_SVEI8MM = 1 << 9
|
||||
hwcap2_SVEF32MM = 1 << 10
|
||||
hwcap2_SVEF64MM = 1 << 11
|
||||
hwcap2_SVEBF16 = 1 << 12
|
||||
hwcap2_I8MM = 1 << 13
|
||||
hwcap2_BF16 = 1 << 14
|
||||
hwcap2_DGH = 1 << 15
|
||||
hwcap2_RNG = 1 << 16
|
||||
hwcap2_BTI = 1 << 17
|
||||
hwcap2_MTE = 1 << 18
|
||||
hwcap2_ECV = 1 << 19
|
||||
hwcap2_AFP = 1 << 20
|
||||
hwcap2_RPRES = 1 << 21
|
||||
hwcap2_MTE3 = 1 << 22
|
||||
hwcap2_SME = 1 << 23
|
||||
hwcap2_SME_I16I64 = 1 << 24
|
||||
hwcap2_SME_F64F64 = 1 << 25
|
||||
hwcap2_SME_I8I32 = 1 << 26
|
||||
hwcap2_SME_F16F32 = 1 << 27
|
||||
hwcap2_SME_B16F32 = 1 << 28
|
||||
hwcap2_SME_F32F32 = 1 << 29
|
||||
hwcap2_SME_FA64 = 1 << 30
|
||||
hwcap2_WFXT = 1 << 31
|
||||
hwcap2_EBF16 = 1 << 32
|
||||
hwcap2_SVE_EBF16 = 1 << 33
|
||||
hwcap2_CSSC = 1 << 34
|
||||
hwcap2_RPRFM = 1 << 35
|
||||
hwcap2_SVE2P1 = 1 << 36
|
||||
hwcap2_SME2 = 1 << 37
|
||||
hwcap2_SME2P1 = 1 << 38
|
||||
hwcap2_SME_I16I32 = 1 << 39
|
||||
hwcap2_SME_BI32I32 = 1 << 40
|
||||
hwcap2_SME_B16B16 = 1 << 41
|
||||
hwcap2_SME_F16F16 = 1 << 42
|
||||
hwcap2_MOPS = 1 << 43
|
||||
hwcap2_HBC = 1 << 44
|
||||
hwcap2_SVE_B16B16 = 1 << 45
|
||||
hwcap2_LRCPC3 = 1 << 46
|
||||
hwcap2_LSE128 = 1 << 47
|
||||
hwcap2_FPMR = 1 << 48
|
||||
hwcap2_LUT = 1 << 49
|
||||
hwcap2_FAMINMAX = 1 << 50
|
||||
hwcap2_F8CVT = 1 << 51
|
||||
hwcap2_F8FMA = 1 << 52
|
||||
hwcap2_F8DP4 = 1 << 53
|
||||
hwcap2_F8DP2 = 1 << 54
|
||||
hwcap2_F8E4M3 = 1 << 55
|
||||
hwcap2_F8E5M2 = 1 << 56
|
||||
hwcap2_SME_LUTV2 = 1 << 57
|
||||
hwcap2_SME_F8F16 = 1 << 58
|
||||
hwcap2_SME_F8F32 = 1 << 59
|
||||
hwcap2_SME_SF8FMA = 1 << 60
|
||||
hwcap2_SME_SF8DP4 = 1 << 61
|
||||
hwcap2_SME_SF8DP2 = 1 << 62
|
||||
hwcap2_POE = 1 << 63
|
||||
)
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
// For now assuming no hyperthreading is reasonable.
|
||||
c.LogicalCores = runtime.NumCPU()
|
||||
c.PhysicalCores = c.LogicalCores
|
||||
c.ThreadsPerCore = 1
|
||||
if hwcap == 0 {
|
||||
// We did not get values from the runtime.
|
||||
// Try reading /proc/self/auxv
|
||||
|
||||
// From https://github.com/golang/sys
|
||||
const (
|
||||
_AT_HWCAP = 16
|
||||
_AT_HWCAP2 = 26
|
||||
|
||||
uintSize = int(32 << (^uint(0) >> 63))
|
||||
)
|
||||
|
||||
buf, err := ioutil.ReadFile("/proc/self/auxv")
|
||||
if err != nil {
|
||||
// e.g. on android /proc/self/auxv is not accessible, so silently
|
||||
// ignore the error and leave Initialized = false. On some
|
||||
// architectures (e.g. arm64) doinit() implements a fallback
|
||||
// readout and will set Initialized = true again.
|
||||
return false
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
for len(buf) >= 2*(uintSize/8) {
|
||||
var tag, val uint
|
||||
switch uintSize {
|
||||
case 32:
|
||||
tag = uint(bo.Uint32(buf[0:]))
|
||||
val = uint(bo.Uint32(buf[4:]))
|
||||
buf = buf[8:]
|
||||
case 64:
|
||||
tag = uint(bo.Uint64(buf[0:]))
|
||||
val = uint(bo.Uint64(buf[8:]))
|
||||
buf = buf[16:]
|
||||
}
|
||||
switch tag {
|
||||
case _AT_HWCAP:
|
||||
hwcap = val
|
||||
case _AT_HWCAP2:
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
if hwcap == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// HWCap was populated by the runtime from the auxiliary vector.
|
||||
// Use HWCap information since reading aarch64 system registers
|
||||
// is not supported in user space on older linux kernels.
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_AES), AESARM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMD), ASIMD)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDDP), ASIMDDP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDHP), ASIMDHP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDRDM), ASIMDRDM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_CPUID), ARMCPUID)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_CRC32), CRC32)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_DCPOP), DCPOP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_EVTSTRM), EVTSTRM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FCMA), FCMA)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ASIMDFHM), FHM)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FP), FP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_FPHP), FPHP)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_JSCVT), JSCVT)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_LRCPC), LRCPC)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_PMULL), PMULL)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap2_RNG), RNDR)
|
||||
// c.featureSet.setIf(isSet(hwcap, hwcap_), TLB)
|
||||
// c.featureSet.setIf(isSet(hwcap, hwcap_), TS)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA1), SHA1)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA2), SHA2)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA3), SHA3)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SHA512), SHA512)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SM3), SM3)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SM4), SM4)
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_SVE), SVE)
|
||||
|
||||
// The Samsung S9+ kernel reports support for atomics, but not all cores
|
||||
// actually support them, resulting in SIGILL. See issue #28431.
|
||||
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
|
||||
c.featureSet.setIf(isSet(hwcap, hwcap_ATOMICS) && runtime.GOOS != "android", ATOMICS)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isSet(hwc uint, value uint) bool {
|
||||
return hwc&value != 0
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2020 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build arm64 && !linux && !darwin
|
||||
// +build arm64,!linux,!darwin
|
||||
|
||||
package cpuid
|
||||
|
||||
import "runtime"
|
||||
|
||||
func detectOS(c *CPUInfo) bool {
|
||||
c.PhysicalCores = runtime.NumCPU()
|
||||
// For now assuming 1 thread per core...
|
||||
c.ThreadsPerCore = 1
|
||||
c.LogicalCores = c.PhysicalCores
|
||||
return false
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build nounsafe
|
||||
// +build nounsafe
|
||||
|
||||
package cpuid
|
||||
|
||||
var hwcap uint
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2021 Klaus Post, released under MIT License. See LICENSE file.
|
||||
|
||||
//go:build !nounsafe
|
||||
// +build !nounsafe
|
||||
|
||||
package cpuid
|
||||
|
||||
import _ "unsafe" // needed for go:linkname
|
||||
|
||||
//go:linkname hwcap internal/cpu.HWCap
|
||||
var hwcap uint
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
go tool dist list | while IFS=/ read os arch; do
|
||||
echo "Checking $os/$arch..."
|
||||
echo " normal"
|
||||
GOARCH=$arch GOOS=$os go build -o /dev/null .
|
||||
echo " noasm"
|
||||
GOARCH=$arch GOOS=$os go build -tags noasm -o /dev/null .
|
||||
echo " appengine"
|
||||
GOARCH=$arch GOOS=$os go build -tags appengine -o /dev/null .
|
||||
echo " noasm,appengine"
|
||||
GOARCH=$arch GOOS=$os go build -tags 'appengine noasm' -o /dev/null .
|
||||
done
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
|
||||
FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609
|
||||
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=arm
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602
|
||||
FROM golang:1.26@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609
|
||||
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=arm64
|
||||
|
||||
+4
-23
@@ -1,35 +1,18 @@
|
||||
FUZZ_TIME ?= 1m
|
||||
FUZZ_FLAGS ?=
|
||||
|
||||
# The fuzz targets of each package, along with the tags needed to build them.
|
||||
FUZZ_TARGETS = ./test/:gofuzz .:sha1cd_asmtest
|
||||
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
# The hashing tests run a second time with SHA-NI off, so that CPUs with it
|
||||
# also cover the AVX2 fallback that CPUs without it use.
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race -timeout 15s -tags sha1cd_asmtest ./...
|
||||
SHA1CD_TEST_NOSHANI=1 go test -race -timeout 15s -tags sha1cd_asmtest ./test/
|
||||
go test -race -timeout 15s ./...
|
||||
|
||||
.PHONY: bench
|
||||
bench:
|
||||
go test -benchmem -run=^$$ -bench ^Benchmark ./...
|
||||
|
||||
# go test only fuzzes a single target per invocation, so each one is run in
|
||||
# turn for FUZZ_TIME.
|
||||
.PHONY: fuzz
|
||||
fuzz:
|
||||
@set -e; for entry in $(FUZZ_TARGETS); do \
|
||||
pkg="$${entry%%:*}"; tags="$${entry##*:}"; \
|
||||
listed="$$(go test -tags "$$tags" -list '^Fuzz' "$$pkg")"; \
|
||||
for target in $$(echo "$$listed" | grep '^Fuzz'); do \
|
||||
echo "fuzzing $$target in $$pkg for $(FUZZ_TIME)"; \
|
||||
go test $(FUZZ_FLAGS) -tags "$$tags" -run '^$$' -fuzz "^$$target"'$$' \
|
||||
-fuzztime=$(FUZZ_TIME) "$$pkg"; \
|
||||
done; \
|
||||
done
|
||||
go test -tags gofuzz -fuzz=. -fuzztime=$(FUZZ_TIME) ./test/
|
||||
|
||||
# Cross build project in arm/v7.
|
||||
build-arm:
|
||||
@@ -41,11 +24,9 @@ build-arm64:
|
||||
docker build -t sha1cd-arm64 -f Dockerfile.arm64 .
|
||||
docker run --rm sha1cd-arm64
|
||||
|
||||
# Build with cgo disabled. Vetting every package (not just ./cgo) is what
|
||||
# catches the cgo and non-cgo builds drifting apart in their exported API.
|
||||
# Build with cgo disabled.
|
||||
build-nocgo:
|
||||
CGO_ENABLED=0 go build ./...
|
||||
CGO_ENABLED=0 go vet ./...
|
||||
CGO_ENABLED=0 go build ./cgo
|
||||
|
||||
# Run cross-compilation to assure supported architectures.
|
||||
cross-build: build-arm build-arm64 build-nocgo
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// Package cpu detects the CPU features that sha1cd dispatches on.
|
||||
//
|
||||
// It covers only what the assembly implementations need, which keeps
|
||||
// start up cheap and avoids an external dependency. Every flag is false
|
||||
// where a feature cannot be detected safely, which selects the generic
|
||||
// implementation.
|
||||
package cpu
|
||||
|
||||
// X86 holds the features of the current amd64 CPU. All flags are false on
|
||||
// other architectures.
|
||||
var X86 struct {
|
||||
// HasAVX and HasAVX2 are set only when the OS also preserves the YMM
|
||||
// state, and HasAVX512F only when it preserves the ZMM and opmask state.
|
||||
HasAVX bool
|
||||
HasAVX2 bool
|
||||
HasAVX512F bool
|
||||
HasBMI1 bool
|
||||
HasBMI2 bool
|
||||
HasSHA bool
|
||||
HasSSSE3 bool
|
||||
HasSSE41 bool
|
||||
}
|
||||
|
||||
// ARM64 holds the features of the current arm64 CPU. All flags are false on
|
||||
// other architectures.
|
||||
var ARM64 struct {
|
||||
HasSHA1 bool
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
//go:build !noasm && gc && amd64
|
||||
|
||||
package cpu
|
||||
|
||||
// cpuid and xgetbv are implemented in cpu_amd64.s.
|
||||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||
func xgetbv() (eax, edx uint32)
|
||||
|
||||
func init() {
|
||||
const (
|
||||
// CPUID EAX=1: ECX
|
||||
ssse3 = 1 << 9
|
||||
sse41 = 1 << 19
|
||||
osxsave = 1 << 27
|
||||
avx = 1 << 28
|
||||
|
||||
// CPUID EAX=7, ECX=0: EBX
|
||||
bmi1 = 1 << 3
|
||||
avx2 = 1 << 5
|
||||
bmi2 = 1 << 8
|
||||
avx512f = 1 << 16
|
||||
sha = 1 << 29
|
||||
|
||||
// XCR0
|
||||
xmmState = 1 << 1
|
||||
ymmState = 1 << 2
|
||||
opmaskState = 1 << 5
|
||||
zmmHi256State = 1 << 6
|
||||
hi16ZMMState = 1 << 7
|
||||
zmmState = opmaskState | zmmHi256State | hi16ZMMState
|
||||
)
|
||||
|
||||
maxID, _, _, _ := cpuid(0, 0)
|
||||
if maxID < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, ecx1, _ := cpuid(1, 0)
|
||||
X86.HasSSSE3 = ecx1&ssse3 != 0
|
||||
X86.HasSSE41 = ecx1&sse41 != 0
|
||||
|
||||
// VEX encoded instructions also need the OS to preserve the YMM state,
|
||||
// which XGETBV reports once OSXSAVE says it is available.
|
||||
// macOS enables the ZMM state lazily, so XCR0 may not report it and
|
||||
// AVX-512 is then left unused, which is safe.
|
||||
var osYMM, osZMM bool
|
||||
if ecx1&(osxsave|avx) == osxsave|avx {
|
||||
xcr0, _ := xgetbv()
|
||||
osYMM = xcr0&(xmmState|ymmState) == xmmState|ymmState
|
||||
osZMM = osYMM && xcr0&zmmState == zmmState
|
||||
}
|
||||
X86.HasAVX = osYMM
|
||||
|
||||
if maxID < 7 {
|
||||
return
|
||||
}
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
X86.HasAVX2 = osYMM && ebx7&avx2 != 0
|
||||
X86.HasAVX512F = osZMM && ebx7&avx512f != 0
|
||||
X86.HasBMI1 = ebx7&bmi1 != 0
|
||||
X86.HasBMI2 = ebx7&bmi2 != 0
|
||||
X86.HasSHA = ebx7&sha != 0
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
//go:build !noasm && gc && amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·cpuid(SB), NOSPLIT, $0-24
|
||||
MOVL eaxArg+0(FP), AX
|
||||
MOVL ecxArg+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func xgetbv() (eax, edx uint32)
|
||||
TEXT ·xgetbv(SB), NOSPLIT, $0-8
|
||||
MOVL $0, CX
|
||||
XGETBV
|
||||
MOVL AX, eax+0(FP)
|
||||
MOVL DX, edx+4(FP)
|
||||
RET
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
//go:build !noasm && gc && arm64 && darwin
|
||||
|
||||
package cpu
|
||||
|
||||
// Every Apple arm64 chip implements the ARMv8 Cryptographic Extension. The Go
|
||||
// runtime makes the same assumption for crypto/sha1 on darwin/arm64.
|
||||
func init() {
|
||||
ARM64.HasSHA1 = true
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
//go:build !noasm && gc && arm64 && freebsd
|
||||
|
||||
package cpu
|
||||
|
||||
// getisar0 is implemented in cpu_arm64_freebsd.s.
|
||||
func getisar0() uint64
|
||||
|
||||
// FreeBSD emulates user space reads of ID_AA64ISAR0_EL1. Its SHA1 field, bits
|
||||
// [11:8], is non zero when the SHA1 instructions are implemented.
|
||||
func init() {
|
||||
ARM64.HasSHA1 = (getisar0()>>8)&0xf != 0
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
//go:build !noasm && gc && arm64 && freebsd
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func getisar0() uint64
|
||||
TEXT ·getisar0(SB), NOSPLIT, $0-8
|
||||
MRS ID_AA64ISAR0_EL1, R0
|
||||
MOVD R0, ret+0(FP)
|
||||
RET
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
//go:build !noasm && gc && arm64 && linux
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"os"
|
||||
_ "unsafe" // for go:linkname
|
||||
)
|
||||
|
||||
// runtime_getAuxv returns the auxiliary vector the kernel passed to the
|
||||
// process. The runtime keeps it reachable for golang.org/x/sys/cpu and
|
||||
// others, see go.dev/issue/57336 and go.dev/issue/67401.
|
||||
//
|
||||
//go:linkname runtime_getAuxv runtime.getAuxv
|
||||
func runtime_getAuxv() []uintptr
|
||||
|
||||
// Linux, and so Android, reports the SHA1 instructions through HWCAP, as
|
||||
// not every kernel lets user space read the ID registers.
|
||||
func init() {
|
||||
hwcap, ok := hwcapFromAuxv(runtime_getAuxv())
|
||||
if !ok {
|
||||
// The procfs copy may not be readable in restricted environments,
|
||||
// in which case the generic implementation is used.
|
||||
if buf, err := os.ReadFile("/proc/self/auxv"); err == nil {
|
||||
hwcap, _ = hwcapFromProcAuxv(buf)
|
||||
}
|
||||
}
|
||||
ARM64.HasSHA1 = hwcap&hwcapSHA1 != 0
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user