mirror of https://github.com/containers/conmon.git
Merge pull request #579 from jnovy/bats
Replace Go tests with BATS and remove Go dependency
This commit is contained in:
commit
84edf228f5
|
|
@ -11,14 +11,12 @@ jobs:
|
|||
|
||||
conmon:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [stable, oldstable]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Install BATS
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y bats
|
||||
- run: sudo hack/github-actions-setup
|
||||
- name: Run conmon integration tests
|
||||
run: |
|
||||
|
|
@ -35,6 +33,10 @@ jobs:
|
|||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Install BATS
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y bats
|
||||
- run: sudo hack/github-actions-setup
|
||||
- name: Run CRI-O integration tests
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -12,22 +12,20 @@ permissions:
|
|||
|
||||
jobs:
|
||||
|
||||
deps:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
- name: Verify Go dependencies
|
||||
- name: Check C code formatting
|
||||
run: |
|
||||
make vendor
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-format
|
||||
make fmt
|
||||
git diff --exit-code
|
||||
|
||||
all-done:
|
||||
needs:
|
||||
- deps
|
||||
- lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "All jobs completed"
|
||||
|
|
|
|||
23
Makefile
23
Makefile
|
|
@ -2,8 +2,6 @@ VERSION := $(shell cat VERSION)
|
|||
PREFIX ?= /usr/local
|
||||
BINDIR ?= ${PREFIX}/bin
|
||||
LIBEXECDIR ?= ${PREFIX}/libexec
|
||||
GO ?= go
|
||||
PROJECT := github.com/containers/conmon
|
||||
PKG_CONFIG ?= pkg-config
|
||||
HEADERS := $(wildcard src/*.h)
|
||||
|
||||
|
|
@ -71,28 +69,20 @@ bin/conmon: $(OBJS) | bin
|
|||
%.o: %.c $(HEADERS)
|
||||
$(CC) $(CFLAGS) $(DEBUGFLAG) -o $@ -c $<
|
||||
|
||||
config: git-vars cmd/conmon-config/conmon-config.go runner/config/config.go runner/config/config_unix.go runner/config/config_windows.go
|
||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o bin/config $(PROJECT)/cmd/conmon-config
|
||||
( cd src && $(CURDIR)/bin/config )
|
||||
# config target removed - no longer using Go build system
|
||||
|
||||
.PHONY: test-binary
|
||||
test-binary: bin/conmon _test-files
|
||||
CONMON_BINARY="$(MAKEFILE_PATH)bin/conmon" $(GO) test $(LDFLAGS) -tags "$(BUILDTAGS)" $(PROJECT)/runner/conmon_test/ -count=1 -v
|
||||
test-binary: bin/conmon
|
||||
CONMON_BINARY="$(MAKEFILE_PATH)bin/conmon" test/run-tests.sh
|
||||
|
||||
.PHONY: test
|
||||
test:_test-files
|
||||
$(GO) test $(LDFLAGS) -tags "$(BUILDTAGS)" $(PROJECT)/runner/conmon_test/
|
||||
|
||||
.PHONY: test-files
|
||||
_test-files: git-vars runner/conmon_test/*.go runner/conmon/*.go
|
||||
test: bin/conmon
|
||||
CONMON_BINARY="$(MAKEFILE_PATH)bin/conmon" test/run-tests.sh
|
||||
|
||||
bin:
|
||||
mkdir -p bin
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
$(GO) mod tidy
|
||||
$(GO) mod verify
|
||||
# vendor target removed - no longer using Go modules
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
|
|
@ -126,7 +116,6 @@ install.podman: bin/conmon
|
|||
.PHONY: fmt
|
||||
fmt:
|
||||
git ls-files -z \*.c \*.h | xargs -0 clang-format -i
|
||||
gofmt -s -w .
|
||||
|
||||
|
||||
.PHONY: dbuild
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/containers/conmon/runner/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
output := `
|
||||
#if !defined(CONFIG_H)
|
||||
#define CONFIG_H
|
||||
|
||||
#define BUF_SIZE %d
|
||||
#define STDIO_BUF_SIZE %d
|
||||
#define CONN_SOCK_BUF_SIZE %d
|
||||
#define DEFAULT_SOCKET_PATH "%s"
|
||||
#define WIN_RESIZE_EVENT %d
|
||||
#define REOPEN_LOGS_EVENT %d
|
||||
#define TIMED_OUT_MESSAGE "%s"
|
||||
|
||||
#endif // CONFIG_H
|
||||
`
|
||||
if err := os.WriteFile("config.h", []byte(fmt.Sprintf(
|
||||
output,
|
||||
config.BufSize,
|
||||
config.BufSize,
|
||||
config.ConnSockBufSize,
|
||||
config.ContainerAttachSocketDir,
|
||||
config.WinResizeEvent,
|
||||
config.ReopenLogsEvent,
|
||||
config.TimedOutMessage)),
|
||||
0o644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
31
go.mod
31
go.mod
|
|
@ -1,31 +0,0 @@
|
|||
module github.com/containers/conmon
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/containers/storage v1.48.0
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/onsi/ginkgo/v2 v2.15.0
|
||||
github.com/onsi/gomega v1.31.1
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc
|
||||
golang.org/x/sys v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
82
go.sum
82
go.sum
|
|
@ -1,82 +0,0 @@
|
|||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/containers/storage v1.48.0 h1:wiPs8J2xiFoOEAhxHDRtP6A90Jzj57VqzLRXOqeizns=
|
||||
github.com/containers/storage v1.48.0/go.mod h1:pRp3lkRo2qodb/ltpnudoXggrviRmaCmU5a5GhTBae0=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
|
||||
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
|
||||
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3 h1:l04uafi6kxByhbxev7OWiuUv0LZxEsYUfDWZ6bztAuU=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.3/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc h1:d2hUh5O6MRBvStV55MQ8we08t42zSTqBbscoQccWmMc=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc/go.mod h1:8tx1helyqhUC65McMm3x7HmOex8lO2/v9zPuxmKHurs=
|
||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
@ -49,7 +49,7 @@ install_packages() {
|
|||
. /etc/os-release
|
||||
CRIU_REPO="https://download.opensuse.org/repositories/devel:/tools:/criu/xUbuntu_$VERSION_ID"
|
||||
|
||||
curl -fSsL $CRIU_REPO/Release.key | sudo apt-key add -
|
||||
curl -fSsL $CRIU_REPO/Release.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/criu.gpg
|
||||
echo "deb $CRIU_REPO/ /" | sudo tee /etc/apt/sources.list.d/criu.list
|
||||
|
||||
sudo apt update
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package config
|
||||
|
||||
const (
|
||||
// BufSize is the size of buffers passed in to sockets
|
||||
BufSize = 8192
|
||||
// ConnSockBufSize is the size of the socket used for
|
||||
// to attach to the container
|
||||
ConnSockBufSize = 32768
|
||||
// WinResizeEvent is the event code the caller program will
|
||||
// send along the ctrl fd to signal conmon to resize
|
||||
// the pty window
|
||||
WinResizeEvent = 1
|
||||
// ReopenLogsEvent is the event code the caller program will
|
||||
// send along the ctrl fd to signal conmon to reopen the log files
|
||||
ReopenLogsEvent = 2
|
||||
// TimedOutMessage is the message sent back to the caller by conmon
|
||||
// when a container times out
|
||||
TimedOutMessage = "command timed out"
|
||||
)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package config
|
||||
|
||||
const (
|
||||
ContainerAttachSocketDir = "/var/run/crio"
|
||||
)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//go:build windows
|
||||
|
||||
package config
|
||||
|
||||
const (
|
||||
ContainerAttachSocketDir = "C:\\crio\\run\\"
|
||||
)
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package conmon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var ErrConmonNotStarted = errors.New("conmon instance is not started")
|
||||
|
||||
type ConmonInstance struct {
|
||||
args []string
|
||||
cmd *exec.Cmd
|
||||
started bool
|
||||
path string
|
||||
pidFile string
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
stdin io.Reader
|
||||
|
||||
parentStartPipe *os.File
|
||||
parentAttachPipe *os.File
|
||||
parentSyncPipe *os.File
|
||||
childSyncPipe *os.File
|
||||
childStartPipe *os.File
|
||||
childAttachPipe *os.File
|
||||
}
|
||||
|
||||
func CreateAndExecConmon(options ...ConmonOption) (*ConmonInstance, error) {
|
||||
ci, err := NewConmonInstance(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ci.Start()
|
||||
return ci, nil
|
||||
}
|
||||
|
||||
func NewConmonInstance(options ...ConmonOption) (*ConmonInstance, error) {
|
||||
ci := &ConmonInstance{
|
||||
args: make([]string, 0),
|
||||
}
|
||||
for _, option := range options {
|
||||
if err := option(ci); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO verify path more
|
||||
if ci.path == "" {
|
||||
return nil, errors.New("conmon path not specified")
|
||||
}
|
||||
|
||||
ci.cmd = exec.Command(ci.path, ci.args...)
|
||||
ci.configurePipeEnv()
|
||||
|
||||
ci.cmd.Stdout = ci.stdout
|
||||
ci.cmd.Stderr = ci.stderr
|
||||
ci.cmd.Stdin = ci.stdin
|
||||
return ci, nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Start() error {
|
||||
ci.started = true
|
||||
return ci.cmd.Start()
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Wait() error {
|
||||
if !ci.started {
|
||||
return ErrConmonNotStarted
|
||||
}
|
||||
defer func() {
|
||||
ci.childSyncPipe.Close()
|
||||
ci.childStartPipe.Close()
|
||||
ci.childAttachPipe.Close()
|
||||
}()
|
||||
return ci.cmd.Wait()
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Stdout() (io.Writer, error) {
|
||||
if !ci.started {
|
||||
return nil, ErrConmonNotStarted
|
||||
}
|
||||
return ci.cmd.Stdout, nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Stderr() (io.Writer, error) {
|
||||
if !ci.started {
|
||||
return nil, ErrConmonNotStarted
|
||||
}
|
||||
return ci.cmd.Stderr, nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Pid() (int, error) {
|
||||
if ci.pidFile == "" {
|
||||
return -1, errors.New("conmon pid file not specified")
|
||||
}
|
||||
if !ci.started {
|
||||
return -1, ErrConmonNotStarted
|
||||
}
|
||||
|
||||
pid, err := readConmonPidFile(ci.pidFile)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("failed to get conmon pid: %w", err)
|
||||
}
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
// readConmonPidFile attempts to read conmon's pid from its pid file
|
||||
func readConmonPidFile(pidFile string) (int, error) {
|
||||
// Let's try reading the Conmon pid at the same time.
|
||||
if pidFile != "" {
|
||||
contents, err := os.ReadFile(pidFile)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
// Convert it to an int
|
||||
conmonPID, err := strconv.Atoi(string(contents))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return conmonPID, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) Cleanup() {
|
||||
ci.closePipesOnCleanup()
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
package conmon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type ConmonOption func(*ConmonInstance) error
|
||||
|
||||
func WithVersion() ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--version")
|
||||
}
|
||||
}
|
||||
|
||||
func WithStdout(stdout io.Writer) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
ci.stdout = stdout
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithStderr(stderr io.Writer) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
ci.stderr = stderr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithStdin(stdin io.Reader) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
ci.stdin = stdin
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithPath(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
ci.path = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithContainerID(ctrID string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--cid", ctrID)
|
||||
}
|
||||
}
|
||||
|
||||
func WithContainerUUID(ctrUUID string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--cuuid", ctrUUID)
|
||||
}
|
||||
}
|
||||
|
||||
func WithRuntimePath(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--runtime", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogDriver(driver, path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
fullDriver := path
|
||||
if driver != "" {
|
||||
fullDriver = fmt.Sprintf("%s:%s", driver, path)
|
||||
}
|
||||
return ci.addArgs("--log-path", fullDriver)
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogPath(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--log-path", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithBundlePath(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--bundle", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithSyslog() ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--syslog")
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogLevel(level string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
// TODO verify level is right
|
||||
return ci.addArgs("--log-level", level)
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogSizeMax(sizeMax int64) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
return ci.addArgs("--log-size-max", fmt.Sprintf("%d", sizeMax))
|
||||
}
|
||||
}
|
||||
|
||||
func WithSocketPath(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
// TODO verify path is right
|
||||
// TODO automatically add container ID? right now it's callers responsibility
|
||||
return ci.addArgs("--socket-dir-path", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithContainerPidFile(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
// TODO verify path is right
|
||||
return ci.addArgs("--container-pidfile", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithRuntimeConfig(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
// TODO verify path is right
|
||||
return ci.addArgs("--container-pidfile", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithConmonPidFile(path string) ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
// TODO verify path is right
|
||||
ci.pidFile = path
|
||||
return ci.addArgs("--conmon-pidfile", path)
|
||||
}
|
||||
}
|
||||
|
||||
func WithStartPipe() ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
read, write, err := newPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ci.parentStartPipe = write
|
||||
ci.childStartPipe = read
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithAttachPipe() ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
read, write, err := newPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ci.parentAttachPipe = read
|
||||
ci.childAttachPipe = write
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithSyncPipe() ConmonOption {
|
||||
return func(ci *ConmonInstance) error {
|
||||
read, write, err := newPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ci.parentSyncPipe = read
|
||||
ci.childSyncPipe = write
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// newPipe creates a unix socket pair for communication
|
||||
func newPipe() (read *os.File, write *os.File, err error) {
|
||||
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fds[1]), "read"), os.NewFile(uintptr(fds[0]), "write"), nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) addArgs(args ...string) error {
|
||||
ci.args = append(ci.args, args...)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
package conmon
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// These errors are adapted from github.com/containers/podman:libpod/define
|
||||
// And copied to reduce the vendor surface area of this library
|
||||
var (
|
||||
// ErrInternal indicates an internal library error
|
||||
ErrInternal = fmt.Errorf("internal error")
|
||||
// ErrOCIRuntime indicates a generic error from the OCI runtime
|
||||
ErrOCIRuntime = fmt.Errorf("OCI runtime error")
|
||||
// ErrOCIRuntimePermissionDenied indicates the OCI runtime attempted to invoke a command that returned
|
||||
// a permission denied error
|
||||
ErrOCIRuntimePermissionDenied = fmt.Errorf("OCI permission denied")
|
||||
// ErrOCIRuntimeNotFound indicates the OCI runtime attempted to invoke a command
|
||||
// that was not found
|
||||
ErrOCIRuntimeNotFound = fmt.Errorf("OCI runtime attempted to invoke a command that was not found")
|
||||
)
|
||||
|
||||
func (ci *ConmonInstance) configurePipeEnv() error {
|
||||
if ci.cmd == nil {
|
||||
return errors.New("conmon instance command must be configured")
|
||||
}
|
||||
if ci.started {
|
||||
return errors.New("conmon instance environment cannot be configured after it's started")
|
||||
}
|
||||
// TODO handle PreserveFDs
|
||||
preserveFDs := 0
|
||||
fdCount := 3
|
||||
if ci.childSyncPipe != nil {
|
||||
ci.cmd.Env = append(ci.cmd.Env, fmt.Sprintf("_OCI_SYNCPIPE=%d", preserveFDs+fdCount))
|
||||
ci.cmd.ExtraFiles = append(ci.cmd.ExtraFiles, ci.childSyncPipe)
|
||||
fdCount++
|
||||
}
|
||||
if ci.childStartPipe != nil {
|
||||
ci.cmd.Env = append(ci.cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", preserveFDs+fdCount))
|
||||
ci.cmd.ExtraFiles = append(ci.cmd.ExtraFiles, ci.childStartPipe)
|
||||
fdCount++
|
||||
}
|
||||
if ci.childAttachPipe != nil {
|
||||
ci.cmd.Env = append(ci.cmd.Env, fmt.Sprintf("_OCI_ATTACHPIPE=%d", preserveFDs+fdCount))
|
||||
ci.cmd.ExtraFiles = append(ci.cmd.ExtraFiles, ci.childAttachPipe)
|
||||
fdCount++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) ContainerExitCode() (int, error) {
|
||||
return readConmonPipeData(ci.parentSyncPipe)
|
||||
}
|
||||
|
||||
// readConmonPipeData attempts to read a syncInfo struct from the pipe
|
||||
// TODO podman checks for ociLog capability
|
||||
func readConmonPipeData(pipe *os.File) (int, error) {
|
||||
// syncInfo is used to return data from monitor process to daemon
|
||||
type syncInfo struct {
|
||||
Data int `json:"data"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// Wait to get container pid from conmon
|
||||
type syncStruct struct {
|
||||
si *syncInfo
|
||||
err error
|
||||
}
|
||||
ch := make(chan syncStruct)
|
||||
go func() {
|
||||
var si *syncInfo
|
||||
rdr := bufio.NewReader(pipe)
|
||||
b, err := rdr.ReadBytes('\n')
|
||||
if err != nil {
|
||||
ch <- syncStruct{err: err}
|
||||
}
|
||||
if err := json.Unmarshal(b, &si); err != nil {
|
||||
ch <- syncStruct{err: err}
|
||||
return
|
||||
}
|
||||
ch <- syncStruct{si: si}
|
||||
}()
|
||||
|
||||
data := -1
|
||||
select {
|
||||
case ss := <-ch:
|
||||
if ss.err != nil {
|
||||
return -1, fmt.Errorf("error received on processing data from conmon pipe: %w", ss.err)
|
||||
}
|
||||
if ss.si.Data < 0 {
|
||||
if ss.si.Message != "" {
|
||||
return ss.si.Data, getOCIRuntimeError(ss.si.Message)
|
||||
}
|
||||
return ss.si.Data, fmt.Errorf("conmon invocation failed: %w", ErrInternal)
|
||||
}
|
||||
data = ss.si.Data
|
||||
case <-time.After(1 * time.Minute):
|
||||
return -1, fmt.Errorf("conmon invocation timeout: %w", ErrInternal)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func getOCIRuntimeError(runtimeMsg string) error {
|
||||
// TODO base off of log level
|
||||
// includeFullOutput := logrus.GetLevel() == logrus.DebugLevel
|
||||
includeFullOutput := true
|
||||
|
||||
if match := regexp.MustCompile("(?i).*permission denied.*|.*operation not permitted.*").FindString(runtimeMsg); match != "" {
|
||||
errStr := match
|
||||
if includeFullOutput {
|
||||
errStr = runtimeMsg
|
||||
}
|
||||
return fmt.Errorf("%s: %w", strings.Trim(errStr, "\n"), ErrOCIRuntimePermissionDenied)
|
||||
}
|
||||
if match := regexp.MustCompile("(?i).*executable file not found in.*|.*no such file or directory.*").FindString(runtimeMsg); match != "" {
|
||||
errStr := match
|
||||
if includeFullOutput {
|
||||
errStr = runtimeMsg
|
||||
}
|
||||
return fmt.Errorf("%s: %w", strings.Trim(errStr, "\n"), ErrOCIRuntimeNotFound)
|
||||
}
|
||||
return fmt.Errorf("%s: %w", strings.Trim(runtimeMsg, "\n"), ErrOCIRuntime)
|
||||
}
|
||||
|
||||
func (ci *ConmonInstance) closePipesOnCleanup() {
|
||||
ci.parentSyncPipe.Close()
|
||||
ci.parentStartPipe.Close()
|
||||
ci.parentAttachPipe.Close()
|
||||
}
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
package conmon_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/conmon/runner/conmon"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var _ = Describe("conmon", func() {
|
||||
Describe("version", func() {
|
||||
It("Should return conmon version", func() {
|
||||
out, _ := getConmonOutputGivenOptions(
|
||||
conmon.WithVersion(),
|
||||
conmon.WithPath(conmonPath),
|
||||
)
|
||||
Expect(out).To(ContainSubstring("conmon version"))
|
||||
Expect(out).To(ContainSubstring("commit"))
|
||||
})
|
||||
})
|
||||
Describe("no container ID", func() {
|
||||
It("should fail", func() {
|
||||
_, err := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
)
|
||||
Expect(err).To(ContainSubstring("conmon: Container ID not provided. Use --cid"))
|
||||
})
|
||||
})
|
||||
Describe("no container UUID", func() {
|
||||
It("should fail", func() {
|
||||
_, err := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
)
|
||||
Expect(err).To(ContainSubstring("Container UUID not provided. Use --cuuid"))
|
||||
})
|
||||
})
|
||||
Describe("runtime path", func() {
|
||||
It("no path should fail", func() {
|
||||
_, err := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
)
|
||||
Expect(err).To(ContainSubstring("Runtime path not provided. Use --runtime"))
|
||||
})
|
||||
It("invalid path should fail", func() {
|
||||
_, err := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(invalidPath),
|
||||
)
|
||||
Expect(err).To(ContainSubstring(fmt.Sprintf("Runtime path %s is not valid", invalidPath)))
|
||||
})
|
||||
})
|
||||
Describe("ctr logs", func() {
|
||||
var tmpDir string
|
||||
var tmpLogPath string
|
||||
var origCwd string
|
||||
BeforeEach(func() {
|
||||
tmpDir = GinkgoT().TempDir()
|
||||
tmpLogPath = filepath.Join(tmpDir, "log")
|
||||
var err error
|
||||
origCwd, err = os.Getwd()
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
AfterEach(func() {
|
||||
for {
|
||||
// There is a race condition on the directory deletion
|
||||
// as conmon could still be running and creating files
|
||||
// under tmpDir. Attempt rmdir again if it fails with
|
||||
// ENOTEMPTY.
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil && errors.Is(err, unix.ENOTEMPTY) {
|
||||
continue
|
||||
}
|
||||
Expect(err).To(BeNil())
|
||||
break
|
||||
}
|
||||
Expect(os.RemoveAll(tmpDir)).To(BeNil())
|
||||
err := os.Chdir(origCwd)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("no log driver should fail", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("Log driver not provided. Use --log-path"))
|
||||
})
|
||||
It("empty log driver should fail", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath(""),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("log-path must not be empty"))
|
||||
})
|
||||
It("empty log driver and path should fail", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath(":"),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("log-path must not be empty"))
|
||||
})
|
||||
It("k8s-file requires a filename", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath("k8s-file"),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("k8s-file requires a filename"))
|
||||
})
|
||||
It("k8s-file: requires a filename", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath("k8s-file:"),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("k8s-file requires a filename"))
|
||||
})
|
||||
It("log driver as path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("", tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as k8s-file:path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as :path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath(":"+tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as none should pass", func() {
|
||||
direrr := os.Chdir(tmpDir)
|
||||
Expect(direrr).To(BeNil())
|
||||
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("none", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat("none")
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
It("log driver as off should pass", func() {
|
||||
direrr := os.Chdir(tmpDir)
|
||||
Expect(direrr).To(BeNil())
|
||||
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("off", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat("off")
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
It("log driver as null should pass", func() {
|
||||
direrr := os.Chdir(tmpDir)
|
||||
Expect(direrr).To(BeNil())
|
||||
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("null", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat("none")
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
It("log driver as journald should pass", func() {
|
||||
direrr := os.Chdir(tmpDir)
|
||||
Expect(direrr).To(BeNil())
|
||||
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat("journald")
|
||||
Expect(err).NotTo(BeNil())
|
||||
})
|
||||
It("log driver as :journald should pass", func() {
|
||||
direrr := os.Chdir(tmpDir)
|
||||
Expect(direrr).To(BeNil())
|
||||
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogPath(":journald"),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat("journald")
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as journald with short cid should fail", func() {
|
||||
// conmon requires a cid of len > 12
|
||||
shortCtrID := "abcdefghijkl"
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(shortCtrID),
|
||||
conmon.WithContainerUUID(shortCtrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("Container ID must be longer than 12 characters"))
|
||||
})
|
||||
It("log driver as k8s-file with path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as k8s-file with invalid path should fail", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", invalidPath),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("Failed to open log file"))
|
||||
})
|
||||
It("log driver as invalid driver should fail", func() {
|
||||
invalidLogDriver := "invalid"
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver(invalidLogDriver, tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("No such log driver " + invalidLogDriver))
|
||||
})
|
||||
It("log driver as invalid driver with a blank path should fail", func() {
|
||||
invalidLogDriver := "invalid"
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver(invalidLogDriver, ""),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("No such log driver " + invalidLogDriver))
|
||||
})
|
||||
It("multiple log drivers should pass", func() {
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("multiple log drivers with one invalid should fail", func() {
|
||||
invalidLogDriver := "invalid"
|
||||
_, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogDriver(invalidLogDriver, tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("No such log driver " + invalidLogDriver))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
package conmon_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/conmon/runner/conmon"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("conmon ctr logs", func() {
|
||||
var tmpDir string
|
||||
var tmpLogPath string
|
||||
const invalidLogDriver = "invalid"
|
||||
BeforeEach(func() {
|
||||
tmpDir = GinkgoT().TempDir()
|
||||
tmpLogPath = filepath.Join(tmpDir, "log")
|
||||
})
|
||||
It("no log driver should fail", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts()
|
||||
Expect(stderr).To(ContainSubstring("Log driver not provided. Use --log-path"))
|
||||
})
|
||||
It("log driver as path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("", tmpLogPath))
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as journald should pass", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("journald", ""))
|
||||
Expect(stderr).To(BeEmpty())
|
||||
})
|
||||
It("log driver as journald with short cid should fail", func() {
|
||||
// conmon requires a cid of len > 12
|
||||
shortCtrID := "abcdefghijkl"
|
||||
|
||||
_, stderr := getConmonOutputGivenLogOpts(
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
conmon.WithContainerID(shortCtrID),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("Container ID must be longer than 12 characters"))
|
||||
})
|
||||
It("log driver as k8s-file with path should pass", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("k8s-file", tmpLogPath))
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("log driver as passthrough should pass", func() {
|
||||
stdout, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("passthrough", ""))
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
})
|
||||
It("log driver as k8s-file with invalid path should fail", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("k8s-file", invalidPath))
|
||||
Expect(stderr).To(ContainSubstring("Failed to open log file"))
|
||||
})
|
||||
It("log driver as invalid driver should fail", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver(invalidLogDriver, tmpLogPath))
|
||||
Expect(stderr).To(ContainSubstring("No such log driver " + invalidLogDriver))
|
||||
})
|
||||
It("multiple log drivers should pass", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
)
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
It("multiple log drivers with one invalid should fail", func() {
|
||||
_, stderr := getConmonOutputGivenLogOpts(
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogDriver(invalidLogDriver, tmpLogPath),
|
||||
)
|
||||
Expect(stderr).To(ContainSubstring("No such log driver " + invalidLogDriver))
|
||||
})
|
||||
})
|
||||
|
||||
func getConmonOutputGivenLogOpts(logDriverOpts ...conmon.ConmonOption) (string, string) {
|
||||
opts := []conmon.ConmonOption{
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
}
|
||||
opts = append(opts, logDriverOpts...)
|
||||
return getConmonOutputGivenOptions(opts...)
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
k8s_log_rotation_test.go
|
||||
|
||||
This test suite validates the k8s-file log rotation fix implemented in commit 29d17be.
|
||||
The fix addressed log corruption during log rotation where writev_buffer_flush() was
|
||||
incorrectly handling partial writes, causing corrupted buffer state to carry over to
|
||||
new file descriptors after rotation.
|
||||
|
||||
The tests focus on:
|
||||
1. Basic k8s-file log driver functionality with log-size-max option
|
||||
2. Validation that small log size limits are accepted without errors
|
||||
3. Edge case testing with very small rotation thresholds
|
||||
4. Log file creation and content integrity validation
|
||||
|
||||
While these tests don't create actual running containers (to avoid test environment
|
||||
dependencies), they validate that the conmon command-line options work correctly and
|
||||
that log files can be created and managed properly. The real fix prevents buffer
|
||||
corruption during writev operations when log rotation occurs, which would have
|
||||
manifested as malformed k8s log entries with repeated timestamps and broken formatting.
|
||||
*/
|
||||
|
||||
package conmon_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/conmon/runner/conmon"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("k8s-file log rotation", func() {
|
||||
var tmpDir string
|
||||
var tmpLogPath string
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir = GinkgoT().TempDir()
|
||||
tmpLogPath = filepath.Join(tmpDir, "container.log")
|
||||
})
|
||||
|
||||
// Test that k8s-file log driver creates properly formatted logs
|
||||
It("should create valid k8s log format", func() {
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// Verify that log file exists
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
})
|
||||
|
||||
// Test that log size max option is accepted without errors
|
||||
It("should accept log-size-max option", func() {
|
||||
logSizeMax := int64(1024)
|
||||
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogSizeMax(logSizeMax),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// Verify that log file exists
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
})
|
||||
|
||||
// Test that multiple log drivers work with size limits
|
||||
It("should handle multiple log drivers with size limits", func() {
|
||||
logSizeMax := int64(2048)
|
||||
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogDriver("journald", ""),
|
||||
conmon.WithLogSizeMax(logSizeMax),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// Verify that log file exists
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
})
|
||||
|
||||
// Test the actual log rotation fix - this is what the reviewer requested
|
||||
Describe("log rotation validation", func() {
|
||||
It("should create log file and accept small log size limits for k8s-file driver", func() {
|
||||
// Set a very small max size to test the fix
|
||||
logSizeMax := int64(100) // Very small to test edge cases
|
||||
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogSizeMax(logSizeMax),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// Verify that log file exists (even if empty)
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
})
|
||||
|
||||
It("should handle extremely small rotation limits without crashing", func() {
|
||||
// Test with minimal log size to stress test the rotation logic
|
||||
logSizeMax := int64(50) // Very small
|
||||
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogSizeMax(logSizeMax),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// The main point is that conmon doesn't crash with very small limits
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
})
|
||||
|
||||
It("should properly validate log-size-max parameter bounds", func() {
|
||||
// Test various edge cases for log size max
|
||||
testCases := []int64{1, 10, 100, 1024, 10240}
|
||||
|
||||
for _, size := range testCases {
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogSizeMax(size),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty(), fmt.Sprintf("Should accept log-size-max=%d", size))
|
||||
Expect(stderr).To(BeEmpty(), fmt.Sprintf("Should not error with log-size-max=%d", size))
|
||||
}
|
||||
})
|
||||
|
||||
It("should create log files that can handle simulated k8s format content", func() {
|
||||
// Create a test that verifies the fix would prevent corruption
|
||||
// This test validates that the log file creation and handling works properly
|
||||
logSizeMax := int64(1024) // Reasonable size for testing
|
||||
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(validPath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithLogSizeMax(logSizeMax),
|
||||
)
|
||||
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
// Verify log file exists
|
||||
_, err := os.Stat(tmpLogPath)
|
||||
Expect(err).To(BeNil(), "Log file should be created")
|
||||
|
||||
// Simulate writing k8s format log entries to test the file is ready
|
||||
// This is what the fix addresses - proper log file state management
|
||||
testLogContent := `2023-07-23T18:00:00.000000000Z stdout F Log entry 1: Test message
|
||||
2023-07-23T18:00:01.000000000Z stdout F Log entry 2: Another test message
|
||||
2023-07-23T18:00:02.000000000Z stdout F Log entry 3: Final test message
|
||||
`
|
||||
err = os.WriteFile(tmpLogPath, []byte(testLogContent), 0644)
|
||||
Expect(err).To(BeNil(), "Should be able to write to log file")
|
||||
|
||||
// Verify we can read back the content
|
||||
content, err := os.ReadFile(tmpLogPath)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(string(content)).To(Equal(testLogContent), "Log content should be preserved")
|
||||
|
||||
// This test ensures the log file infrastructure works correctly
|
||||
// The actual fix prevents corruption when conmon handles the writev buffer
|
||||
// during log rotation, which would have caused malformed log entries
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
package conmon_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containers/conmon/runner/conmon"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
)
|
||||
|
||||
var _ = Describe("runc", func() {
|
||||
var (
|
||||
tmpDir string
|
||||
tmpLogPath string
|
||||
tmpPidFile string
|
||||
tmpRootfs string
|
||||
)
|
||||
BeforeEach(func() {
|
||||
// save busy box binary if we don't have it
|
||||
Expect(cacheBusyBox()).To(BeNil())
|
||||
|
||||
// create tmpDir
|
||||
tmpDir = GinkgoT().TempDir()
|
||||
|
||||
// generate logging path
|
||||
tmpLogPath = filepath.Join(tmpDir, "log")
|
||||
|
||||
// generate container ID
|
||||
ctrID = stringid.GenerateNonCryptoID()
|
||||
|
||||
// create the rootfs of the "container"
|
||||
tmpRootfs = filepath.Join(tmpDir, "rootfs")
|
||||
Expect(os.MkdirAll(tmpRootfs, 0o755)).To(BeNil())
|
||||
|
||||
tmpPidFile = filepath.Join(tmpDir, "pidfile")
|
||||
|
||||
busyboxPath := filepath.Join(tmpRootfs, "busybox")
|
||||
Expect(os.Link(busyboxDest, busyboxPath)).To(BeNil())
|
||||
Expect(os.Chmod(busyboxPath, 0o777)).To(BeNil())
|
||||
|
||||
// finally, create config.json
|
||||
_, err := generateRuntimeConfig(tmpDir, tmpRootfs)
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
AfterEach(func() {
|
||||
Expect(runRuntimeCommand("delete", "-f", ctrID)).To(BeNil())
|
||||
})
|
||||
It("simple runtime test", func() {
|
||||
stdout, stderr := getConmonOutputGivenOptions(
|
||||
conmon.WithPath(conmonPath),
|
||||
conmon.WithContainerID(ctrID),
|
||||
conmon.WithContainerUUID(ctrID),
|
||||
conmon.WithRuntimePath(runtimePath),
|
||||
conmon.WithLogDriver("k8s-file", tmpLogPath),
|
||||
conmon.WithBundlePath(tmpDir),
|
||||
conmon.WithSocketPath(tmpDir),
|
||||
conmon.WithSyslog(),
|
||||
conmon.WithLogLevel("trace"),
|
||||
conmon.WithContainerPidFile(tmpPidFile),
|
||||
conmon.WithConmonPidFile(fmt.Sprintf("%s/conmon-pidfile", tmpDir)),
|
||||
conmon.WithSyncPipe(),
|
||||
)
|
||||
Expect(stdout).To(BeEmpty())
|
||||
Expect(stderr).To(BeEmpty())
|
||||
|
||||
Expect(runRuntimeCommand("start", ctrID)).To(BeNil())
|
||||
// Make sure we write the file before checking if it was written
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
Expect(getFileContents(tmpLogPath)).To(ContainSubstring("busybox"))
|
||||
Expect(getFileContents(tmpPidFile)).To(Not(BeEmpty()))
|
||||
})
|
||||
})
|
||||
|
||||
func getFileContents(filename string) string {
|
||||
b, err := os.ReadFile(filename)
|
||||
Expect(err).To(BeNil())
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func generateRuntimeConfig(bundlePath, rootfs string) (string, error) {
|
||||
configPath := filepath.Join(bundlePath, "config.json")
|
||||
g, err := generate.New("linux")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
g.SetProcessCwd("/")
|
||||
g.SetProcessArgs([]string{"/busybox", "echo", "busybox"})
|
||||
g.SetRootPath(rootfs)
|
||||
|
||||
if err := g.SaveToFile(configPath, generate.ExportOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return configPath, nil
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
package conmon_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/conmon/runner/conmon"
|
||||
"github.com/coreos/go-systemd/v22/sdjournal"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
conmonPath = "/usr/bin/conmon"
|
||||
runtimePath = "/usr/bin/runc"
|
||||
busyboxSource = "https://busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox"
|
||||
busyboxDestDir = "/tmp/conmon-test-images"
|
||||
busyboxDest = "/tmp/conmon-test-images/busybox"
|
||||
ctrID = "abcdefghijklm"
|
||||
validPath = "/tmp"
|
||||
invalidPath = "/not/a/path"
|
||||
)
|
||||
|
||||
func TestConmon(t *testing.T) {
|
||||
configureSuiteFromEnv()
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Conmon Suite")
|
||||
}
|
||||
|
||||
func getConmonOutputGivenOptions(options ...conmon.ConmonOption) (string, string) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
var stdin bytes.Buffer
|
||||
|
||||
options = append(options, conmon.WithStdout(&stdout), conmon.WithStderr(&stderr), conmon.WithStdin(&stdin))
|
||||
|
||||
ci, err := conmon.CreateAndExecConmon(options...)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
defer ci.Cleanup()
|
||||
|
||||
ci.Wait()
|
||||
|
||||
pid, _ := ci.Pid()
|
||||
if pid < 0 {
|
||||
return stdout.String(), stderr.String()
|
||||
}
|
||||
|
||||
_, err = ci.ContainerExitCode()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
journalerr, err := getConmonJournalOutput(pid, 3)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
alljournalout, err := getConmonJournalOutput(pid, -1)
|
||||
Expect(err).To(BeNil())
|
||||
fmt.Fprintln(GinkgoWriter, alljournalout)
|
||||
|
||||
return stdout.String(), stderr.String() + journalerr
|
||||
}
|
||||
|
||||
func getConmonJournalOutput(pid int, level int) (string, error) {
|
||||
matches := []sdjournal.Match{
|
||||
{
|
||||
Field: sdjournal.SD_JOURNAL_FIELD_COMM,
|
||||
Value: "conmon",
|
||||
},
|
||||
{
|
||||
Field: sdjournal.SD_JOURNAL_FIELD_PID,
|
||||
Value: strconv.Itoa(pid),
|
||||
},
|
||||
}
|
||||
if level > 0 {
|
||||
matches = append(matches, sdjournal.Match{
|
||||
Field: sdjournal.SD_JOURNAL_FIELD_PRIORITY,
|
||||
Value: strconv.Itoa(level),
|
||||
})
|
||||
}
|
||||
r, err := sdjournal.NewJournalReader(sdjournal.JournalReaderConfig{
|
||||
Matches: matches,
|
||||
Formatter: formatter,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return readAllFromBuffer(r)
|
||||
}
|
||||
|
||||
func formatter(entry *sdjournal.JournalEntry) (string, error) {
|
||||
return entry.Fields[sdjournal.SD_JOURNAL_FIELD_MESSAGE], nil
|
||||
}
|
||||
|
||||
func readAllFromBuffer(r io.ReadCloser) (string, error) {
|
||||
bufLen := 16384
|
||||
stringOutput := ""
|
||||
|
||||
bytes := make([]byte, bufLen)
|
||||
// /me complains about no do-while in go
|
||||
ec, err := r.Read(bytes)
|
||||
for ec != 0 && err == nil {
|
||||
// because we are reusing bytes, we need to make
|
||||
// sure the old data doesn't get into the new line
|
||||
bytestr := string(bytes[:ec])
|
||||
stringOutput += string(bytestr)
|
||||
ec, err = r.Read(bytes)
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return stringOutput, err
|
||||
}
|
||||
return stringOutput, nil
|
||||
}
|
||||
|
||||
func configureSuiteFromEnv() {
|
||||
if path := os.Getenv("CONMON_BINARY"); path != "" {
|
||||
conmonPath = path
|
||||
}
|
||||
if path := os.Getenv("RUNTIME_BINARY"); path != "" {
|
||||
runtimePath = path
|
||||
}
|
||||
}
|
||||
|
||||
func cacheBusyBox() error {
|
||||
if _, err := os.Stat(busyboxDest); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(busyboxDestDir, 0o755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := downloadFile(busyboxSource, busyboxDest); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(busyboxDest, 0o777); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// source: https://progolang.com/how-to-download-files-in-go/
|
||||
// downloadFile will download a url and store it in local filepath.
|
||||
// It writes to the destination file as it downloads it, without
|
||||
// loading the entire file into memory.
|
||||
func downloadFile(url string, filepath string) error {
|
||||
// Create the file
|
||||
out, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Get the data
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runRuntimeCommand(args ...string) error {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
cmd := exec.Command(runtimePath, args...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Run()
|
||||
stdoutString := stdout.String()
|
||||
if stdoutString != "" {
|
||||
fmt.Fprintln(GinkgoWriter, stdoutString)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
check_conmon_binary
|
||||
setup_test_env
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cleanup_test_env
|
||||
}
|
||||
|
||||
@test "conmon version" {
|
||||
run_conmon --version
|
||||
assert_success
|
||||
assert_output_contains "conmon version"
|
||||
assert_output_contains "commit"
|
||||
}
|
||||
|
||||
@test "no container ID should fail" {
|
||||
run_conmon
|
||||
assert_failure
|
||||
assert_output_contains "Container ID not provided. Use --cid"
|
||||
}
|
||||
|
||||
@test "no container UUID should fail" {
|
||||
run_conmon --cid "$CTR_ID"
|
||||
assert_failure
|
||||
assert_output_contains "Container UUID not provided. Use --cuuid"
|
||||
}
|
||||
|
||||
@test "no runtime path should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID"
|
||||
assert_failure
|
||||
assert_output_contains "Runtime path not provided. Use --runtime"
|
||||
}
|
||||
|
||||
@test "invalid runtime path should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$INVALID_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "Runtime path $INVALID_PATH is not valid"
|
||||
}
|
||||
|
||||
@test "no log driver should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "Log driver not provided. Use --log-path"
|
||||
}
|
||||
|
||||
@test "empty log driver should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path ""
|
||||
assert_failure
|
||||
assert_output_contains "log-path must not be empty"
|
||||
}
|
||||
|
||||
@test "empty log driver and path should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path ":"
|
||||
assert_failure
|
||||
assert_output_contains "log-path must not be empty"
|
||||
}
|
||||
|
||||
@test "k8s-file requires a filename" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "k8s-file"
|
||||
assert_failure
|
||||
assert_output_contains "k8s-file requires a filename"
|
||||
}
|
||||
|
||||
@test "k8s-file: requires a filename" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "k8s-file:"
|
||||
assert_failure
|
||||
assert_output_contains "k8s-file requires a filename"
|
||||
}
|
||||
|
||||
@test "log driver as path should pass" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "$LOG_PATH"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "log driver as k8s-file:path should pass" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "k8s-file:$LOG_PATH"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "log driver as :path should pass" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path ":$LOG_PATH"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "log driver as none should pass" {
|
||||
cd "$TEST_TMPDIR"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "none:"
|
||||
assert_success
|
||||
[ ! -f "none" ]
|
||||
}
|
||||
|
||||
@test "log driver as off should pass" {
|
||||
cd "$TEST_TMPDIR"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "off:"
|
||||
assert_success
|
||||
[ ! -f "off" ]
|
||||
}
|
||||
|
||||
@test "log driver as null should pass" {
|
||||
cd "$TEST_TMPDIR"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "null:"
|
||||
assert_success
|
||||
[ ! -f "null" ]
|
||||
}
|
||||
|
||||
@test "log driver as journald should pass" {
|
||||
cd "$TEST_TMPDIR"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "journald:"
|
||||
assert_success
|
||||
[ ! -f "journald" ]
|
||||
}
|
||||
|
||||
@test "log driver as :journald should pass" {
|
||||
cd "$TEST_TMPDIR"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path ":journald"
|
||||
assert_success
|
||||
[ -f "journald" ]
|
||||
}
|
||||
|
||||
@test "log driver as journald with short cid should fail" {
|
||||
local short_ctr_id="abcdefghijkl"
|
||||
run_conmon --cid "$short_ctr_id" --cuuid "$short_ctr_id" --runtime "$VALID_PATH" --log-path "journald:"
|
||||
assert_failure
|
||||
assert_output_contains "Container ID must be longer than 12 characters"
|
||||
}
|
||||
|
||||
@test "log driver as k8s-file with path should pass" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "k8s-file:$LOG_PATH"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "log driver as k8s-file with invalid path should fail" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "k8s-file:$INVALID_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "Failed to open log file"
|
||||
}
|
||||
|
||||
@test "log driver as invalid driver should fail" {
|
||||
local invalid_log_driver="invalid"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "$invalid_log_driver:$LOG_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "No such log driver $invalid_log_driver"
|
||||
}
|
||||
|
||||
@test "log driver as invalid driver with blank path should fail" {
|
||||
local invalid_log_driver="invalid"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" --log-path "$invalid_log_driver:"
|
||||
assert_failure
|
||||
assert_output_contains "No such log driver $invalid_log_driver"
|
||||
}
|
||||
|
||||
@test "multiple log drivers should pass" {
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" \
|
||||
--log-path "k8s-file:$LOG_PATH" --log-path "journald:"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "multiple log drivers with one invalid should fail" {
|
||||
local invalid_log_driver="invalid"
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" \
|
||||
--log-path "k8s-file:$LOG_PATH" --log-path "$invalid_log_driver:$LOG_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "No such log driver $invalid_log_driver"
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
check_conmon_binary
|
||||
setup_test_env
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cleanup_test_env
|
||||
}
|
||||
|
||||
# Helper function to run conmon with basic log options
|
||||
run_conmon_with_log_opts() {
|
||||
local extra_args=("$@")
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" "${extra_args[@]}"
|
||||
}
|
||||
|
||||
@test "ctr logs: no log driver should fail" {
|
||||
run_conmon_with_log_opts
|
||||
assert_failure
|
||||
assert_output_contains "Log driver not provided. Use --log-path"
|
||||
}
|
||||
|
||||
@test "ctr logs: log driver as path should pass" {
|
||||
run_conmon_with_log_opts --log-path "$LOG_PATH"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "ctr logs: log driver as journald should pass" {
|
||||
run_conmon_with_log_opts --log-path "journald:"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "ctr logs: log driver as passthrough should pass" {
|
||||
run_conmon_with_log_opts --log-path "passthrough:"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "ctr logs: log driver as k8s-file with invalid path should fail" {
|
||||
run_conmon_with_log_opts --log-path "k8s-file:$INVALID_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "Failed to open log file"
|
||||
}
|
||||
|
||||
@test "ctr logs: log driver as invalid driver should fail" {
|
||||
local invalid_log_driver="invalid"
|
||||
run_conmon_with_log_opts --log-path "$invalid_log_driver:$LOG_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "No such log driver $invalid_log_driver"
|
||||
}
|
||||
|
||||
@test "ctr logs: multiple log drivers should pass" {
|
||||
run_conmon_with_log_opts --log-path "k8s-file:$LOG_PATH" --log-path "journald:"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "ctr logs: multiple log drivers with one invalid should fail" {
|
||||
local invalid_log_driver="invalid"
|
||||
run_conmon_with_log_opts --log-path "k8s-file:$LOG_PATH" --log-path "$invalid_log_driver:$LOG_PATH"
|
||||
assert_failure
|
||||
assert_output_contains "No such log driver $invalid_log_driver"
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
# k8s_log_rotation_test.bats
|
||||
#
|
||||
# This test suite validates the k8s-file log rotation fix implemented in commit 29d17be.
|
||||
# The fix addressed log corruption during log rotation where writev_buffer_flush() was
|
||||
# incorrectly handling partial writes, causing corrupted buffer state to carry over to
|
||||
# new file descriptors after rotation.
|
||||
#
|
||||
# The tests focus on:
|
||||
# 1. Basic k8s-file log driver functionality with log-size-max option
|
||||
# 2. Validation that small log size limits are accepted without errors
|
||||
# 3. Edge case testing with very small rotation thresholds
|
||||
# 4. Log file creation and content integrity validation
|
||||
#
|
||||
# While these tests don't create actual running containers (to avoid test environment
|
||||
# dependencies), they validate that the conmon command-line options work correctly and
|
||||
# that log files can be created and managed properly. The real fix prevents buffer
|
||||
# corruption during writev operations when log rotation occurs, which would have
|
||||
# manifested as malformed k8s log entries with repeated timestamps and broken formatting.
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
check_conmon_binary
|
||||
setup_test_env
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cleanup_test_env
|
||||
}
|
||||
|
||||
# Helper function to run conmon with k8s-file log driver
|
||||
run_conmon_k8s_file() {
|
||||
local extra_args=("$@")
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" \
|
||||
--log-path "k8s-file:$LOG_PATH" "${extra_args[@]}"
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should create valid k8s log format" {
|
||||
run_conmon_k8s_file
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should accept log-size-max option" {
|
||||
local log_size_max=1024
|
||||
run_conmon_k8s_file --log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should handle multiple log drivers with size limits" {
|
||||
local log_size_max=2048
|
||||
run_conmon --cid "$CTR_ID" --cuuid "$CTR_ID" --runtime "$VALID_PATH" \
|
||||
--log-path "k8s-file:$LOG_PATH" --log-path "journald:" \
|
||||
--log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should create log file and accept small log size limits" {
|
||||
local log_size_max=100 # Very small to test edge cases
|
||||
run_conmon_k8s_file --log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should handle extremely small rotation limits without crashing" {
|
||||
local log_size_max=50 # Very small
|
||||
run_conmon_k8s_file --log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should properly validate log-size-max parameter bounds" {
|
||||
local test_cases=(1 10 100 1024 10240)
|
||||
|
||||
for size in "${test_cases[@]}"; do
|
||||
run_conmon_k8s_file --log-size-max "$size"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Clean up log file for next iteration
|
||||
rm -f "$LOG_PATH"
|
||||
done
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should create log files that can handle simulated k8s format content" {
|
||||
local log_size_max=1024 # Reasonable size for testing
|
||||
|
||||
run_conmon_k8s_file --log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Simulate writing k8s format log entries to test the file is ready
|
||||
# This is what the fix addresses - proper log file state management
|
||||
local test_log_content='2023-07-23T18:00:00.000000000Z stdout F Log entry 1: Test message
|
||||
2023-07-23T18:00:01.000000000Z stdout F Log entry 2: Another test message
|
||||
2023-07-23T18:00:02.000000000Z stdout F Log entry 3: Final test message'
|
||||
|
||||
echo "$test_log_content" > "$LOG_PATH"
|
||||
|
||||
# Verify we can read back the content
|
||||
local content
|
||||
content=$(<"$LOG_PATH")
|
||||
[ "$content" = "$test_log_content" ]
|
||||
|
||||
# This test ensures the log file infrastructure works correctly
|
||||
# The actual fix prevents corruption when conmon handles the writev buffer
|
||||
# during log rotation, which would have caused malformed log entries
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should handle zero log-size-max gracefully" {
|
||||
# Test with zero to ensure no division by zero or other edge case issues
|
||||
run_conmon_k8s_file --log-size-max 0
|
||||
# This might fail or succeed depending on implementation,
|
||||
# but should not crash
|
||||
# We just verify conmon doesn't crash
|
||||
[[ "$status" -eq 0 || "$status" -eq 1 ]]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should handle negative log-size-max gracefully" {
|
||||
# Test with negative value to ensure proper validation
|
||||
run_conmon_k8s_file --log-size-max -1
|
||||
# This should likely fail with validation error, but not crash
|
||||
[[ "$status" -eq 0 || "$status" -eq 1 ]]
|
||||
}
|
||||
|
||||
@test "k8s log rotation: should work with very large log-size-max" {
|
||||
local log_size_max=$((1024 * 1024 * 1024)) # 1GB
|
||||
run_conmon_k8s_file --log-size-max "$log_size_max"
|
||||
assert_success
|
||||
[ -f "$LOG_PATH" ]
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
check_conmon_binary
|
||||
check_runtime_binary
|
||||
setup_container_env
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cleanup_test_env
|
||||
}
|
||||
|
||||
@test "runtime: simple runtime test" {
|
||||
# Run conmon which will create and manage the container
|
||||
# Using a timeout to prevent hanging
|
||||
timeout 30s "$CONMON_BINARY" \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "$RUNTIME_BINARY" \
|
||||
--log-path "k8s-file:$LOG_PATH" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--log-level debug \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE" &
|
||||
|
||||
local conmon_pid=$!
|
||||
|
||||
# Give conmon time to start up and run the container
|
||||
sleep 2
|
||||
|
||||
# Check if conmon is still running or completed
|
||||
if kill -0 $conmon_pid 2>/dev/null; then
|
||||
# Kill conmon if it's still running
|
||||
kill $conmon_pid 2>/dev/null || true
|
||||
wait $conmon_pid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check that log file was created
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Check that conmon pidfile was created
|
||||
[ -f "$CONMON_PID_FILE" ]
|
||||
}
|
||||
|
||||
@test "runtime: container execution with different log drivers" {
|
||||
# Test with journald log driver
|
||||
timeout 30s "$CONMON_BINARY" \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "$RUNTIME_BINARY" \
|
||||
--log-path "journald:" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE" &
|
||||
|
||||
local conmon_pid=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 $conmon_pid 2>/dev/null; then
|
||||
kill $conmon_pid 2>/dev/null || true
|
||||
wait $conmon_pid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check that conmon pidfile was created
|
||||
[ -f "$CONMON_PID_FILE" ]
|
||||
}
|
||||
|
||||
@test "runtime: container execution with multiple log drivers" {
|
||||
# Test with both k8s-file and journald log drivers
|
||||
timeout 30s "$CONMON_BINARY" \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "$RUNTIME_BINARY" \
|
||||
--log-path "k8s-file:$LOG_PATH" \
|
||||
--log-path "journald:" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE" &
|
||||
|
||||
local conmon_pid=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 $conmon_pid 2>/dev/null; then
|
||||
kill $conmon_pid 2>/dev/null || true
|
||||
wait $conmon_pid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check that log file was created
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Check that conmon pidfile was created
|
||||
[ -f "$CONMON_PID_FILE" ]
|
||||
}
|
||||
|
||||
@test "runtime: container with log size limit" {
|
||||
# Test container execution with log rotation
|
||||
local log_size_max=1024
|
||||
|
||||
timeout 30s "$CONMON_BINARY" \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "$RUNTIME_BINARY" \
|
||||
--log-path "k8s-file:$LOG_PATH" \
|
||||
--log-size-max "$log_size_max" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE" &
|
||||
|
||||
local conmon_pid=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 $conmon_pid 2>/dev/null; then
|
||||
kill $conmon_pid 2>/dev/null || true
|
||||
wait $conmon_pid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check that log file was created
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Check that conmon pidfile was created
|
||||
[ -f "$CONMON_PID_FILE" ]
|
||||
}
|
||||
|
||||
@test "runtime: container cleanup on completion" {
|
||||
# Create and run a container, then verify cleanup
|
||||
timeout 30s "$CONMON_BINARY" \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "$RUNTIME_BINARY" \
|
||||
--log-path "k8s-file:$LOG_PATH" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE" &
|
||||
|
||||
local conmon_pid=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 $conmon_pid 2>/dev/null; then
|
||||
kill $conmon_pid 2>/dev/null || true
|
||||
wait $conmon_pid 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Check that log file was created
|
||||
[ -f "$LOG_PATH" ]
|
||||
|
||||
# Check that conmon pidfile was created
|
||||
[ -f "$CONMON_PID_FILE" ]
|
||||
}
|
||||
|
||||
@test "runtime: invalid runtime binary should fail" {
|
||||
# Test with non-existent runtime binary
|
||||
run_conmon \
|
||||
--cid "$CTR_ID" \
|
||||
--cuuid "$CTR_ID" \
|
||||
--runtime "/nonexistent/runtime" \
|
||||
--log-path "k8s-file:$LOG_PATH" \
|
||||
--bundle "$BUNDLE_PATH" \
|
||||
--socket-dir-path "$SOCKET_PATH" \
|
||||
--container-pidfile "$PID_FILE" \
|
||||
--conmon-pidfile "$CONMON_PID_FILE"
|
||||
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "runtime: configuration validation works" {
|
||||
# Test that conmon can validate its configuration
|
||||
# This is a basic smoke test for the runtime integration
|
||||
run_conmon --version
|
||||
assert_success
|
||||
assert_output_contains "conmon version"
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Test runner script for conmon BATS tests
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Default values
|
||||
CONMON_BINARY="${CONMON_BINARY:-$PROJECT_ROOT/bin/conmon}"
|
||||
RUNTIME_BINARY="${RUNTIME_BINARY:-/usr/bin/runc}"
|
||||
BATS_OPTIONS="${BATS_OPTIONS:-}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS] [TEST_FILES...]
|
||||
|
||||
Run conmon BATS tests.
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-c, --conmon BINARY Path to conmon binary (default: $CONMON_BINARY)
|
||||
-r, --runtime BINARY Path to runtime binary (default: $RUNTIME_BINARY)
|
||||
-v, --verbose Verbose output
|
||||
-t, --tap Output in TAP format
|
||||
-j, --jobs N Run tests in parallel with N jobs
|
||||
--filter PATTERN Run only tests matching PATTERN
|
||||
|
||||
EXAMPLES:
|
||||
$0 Run all tests
|
||||
$0 01-basic.bats Run only basic tests
|
||||
$0 --verbose Run all tests with verbose output
|
||||
$0 --filter "version" Run only tests with 'version' in the name
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
CONMON_BINARY Path to conmon binary
|
||||
RUNTIME_BINARY Path to runtime binary
|
||||
BATS_OPTIONS Additional options to pass to bats
|
||||
EOF
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $*"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $*" >&2
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
local missing_deps=()
|
||||
|
||||
if ! command -v bats >/dev/null 2>&1; then
|
||||
missing_deps+=("bats")
|
||||
fi
|
||||
|
||||
if [[ ! -x "$CONMON_BINARY" ]]; then
|
||||
missing_deps+=("conmon binary at $CONMON_BINARY")
|
||||
fi
|
||||
|
||||
if [[ ! -x "$RUNTIME_BINARY" ]]; then
|
||||
missing_deps+=("runtime binary at $RUNTIME_BINARY")
|
||||
fi
|
||||
|
||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||
log_error "Missing dependencies:"
|
||||
printf ' - %s\n' "${missing_deps[@]}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local verbose=false
|
||||
local tap=false
|
||||
local jobs=""
|
||||
local filter=""
|
||||
local test_files=()
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-c|--conmon)
|
||||
CONMON_BINARY="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--runtime)
|
||||
RUNTIME_BINARY="$2"
|
||||
shift 2
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose=true
|
||||
shift
|
||||
;;
|
||||
-t|--tap)
|
||||
tap=true
|
||||
shift
|
||||
;;
|
||||
-j|--jobs)
|
||||
jobs="$2"
|
||||
shift 2
|
||||
;;
|
||||
--filter)
|
||||
filter="$2"
|
||||
shift 2
|
||||
;;
|
||||
*.bats)
|
||||
test_files+=("$1")
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set up BATS options
|
||||
local bats_args=()
|
||||
|
||||
if [[ "$verbose" == true ]]; then
|
||||
bats_args+=("--verbose-run")
|
||||
fi
|
||||
|
||||
if [[ "$tap" == true ]]; then
|
||||
bats_args+=("--tap")
|
||||
fi
|
||||
|
||||
if [[ -n "$jobs" ]]; then
|
||||
bats_args+=("--jobs" "$jobs")
|
||||
fi
|
||||
|
||||
if [[ -n "$filter" ]]; then
|
||||
bats_args+=("--filter" "$filter")
|
||||
fi
|
||||
|
||||
# Add any additional BATS options from environment
|
||||
if [[ -n "$BATS_OPTIONS" ]]; then
|
||||
read -ra additional_opts <<< "$BATS_OPTIONS"
|
||||
bats_args+=("${additional_opts[@]}")
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
log_info "Checking dependencies..."
|
||||
if ! check_dependencies; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine test files to run
|
||||
if [[ ${#test_files[@]} -eq 0 ]]; then
|
||||
# Run all .bats files in test directory
|
||||
mapfile -t test_files < <(find "$SCRIPT_DIR" -name "*.bats" | sort)
|
||||
else
|
||||
# Convert relative paths to absolute paths
|
||||
local resolved_files=()
|
||||
for file in "${test_files[@]}"; do
|
||||
if [[ "$file" =~ ^/ ]]; then
|
||||
resolved_files+=("$file")
|
||||
elif [[ "$file" =~ test/ ]]; then
|
||||
# Already prefixed with test/, use from project root
|
||||
resolved_files+=("$PROJECT_ROOT/$file")
|
||||
else
|
||||
# Add test/ prefix
|
||||
resolved_files+=("$SCRIPT_DIR/$file")
|
||||
fi
|
||||
done
|
||||
test_files=("${resolved_files[@]}")
|
||||
fi
|
||||
|
||||
# Verify test files exist
|
||||
for file in "${test_files[@]}"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "Test file not found: $file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Export environment variables for tests
|
||||
export CONMON_BINARY
|
||||
export RUNTIME_BINARY
|
||||
|
||||
log_info "Running tests with:"
|
||||
log_info " conmon binary: $CONMON_BINARY"
|
||||
log_info " runtime binary: $RUNTIME_BINARY"
|
||||
log_info " test files: ${test_files[*]}"
|
||||
|
||||
# Run the tests
|
||||
log_info "Starting test execution..."
|
||||
if bats "${bats_args[@]}" "${test_files[@]}"; then
|
||||
log_info "All tests passed!"
|
||||
exit 0
|
||||
else
|
||||
log_error "Some tests failed!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Only run main if script is executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Common test helper functions for conmon BATS tests
|
||||
|
||||
# Provide basic assertion functions if not available
|
||||
assert_success() {
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo "Command failed with status $status"
|
||||
echo "Output: $output"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_failure() {
|
||||
if [ "$status" -eq 0 ]; then
|
||||
echo "Command succeeded but failure was expected"
|
||||
echo "Output: $output"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Default paths and variables
|
||||
CONMON_BINARY="${CONMON_BINARY:-/usr/bin/conmon}"
|
||||
RUNTIME_BINARY="${RUNTIME_BINARY:-/usr/bin/runc}"
|
||||
BUSYBOX_SOURCE="https://busybox.net/downloads/binaries/1.31.0-i686-uclibc/busybox"
|
||||
BUSYBOX_DEST_DIR="/tmp/conmon-test-images"
|
||||
BUSYBOX_DEST="/tmp/conmon-test-images/busybox"
|
||||
VALID_PATH="/tmp"
|
||||
INVALID_PATH="/not/a/path"
|
||||
|
||||
# Generate a unique container ID for each test
|
||||
generate_ctr_id() {
|
||||
echo "conmon-test-$(date +%s)-$$-$RANDOM"
|
||||
}
|
||||
|
||||
# Cache busybox binary for container tests
|
||||
cache_busybox() {
|
||||
if [[ -f "$BUSYBOX_DEST" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$BUSYBOX_DEST_DIR"
|
||||
if ! curl -s -L "$BUSYBOX_SOURCE" -o "$BUSYBOX_DEST"; then
|
||||
skip "Failed to download busybox binary"
|
||||
fi
|
||||
chmod +x "$BUSYBOX_DEST"
|
||||
}
|
||||
|
||||
# Run conmon with given arguments and capture output
|
||||
run_conmon() {
|
||||
run "$CONMON_BINARY" "$@"
|
||||
}
|
||||
|
||||
# Run runtime command (runc)
|
||||
run_runtime() {
|
||||
run "$RUNTIME_BINARY" "$@"
|
||||
}
|
||||
|
||||
# Get journal output for conmon process
|
||||
get_conmon_journal_output() {
|
||||
local pid="$1"
|
||||
local level="${2:--1}"
|
||||
|
||||
if ! command -v journalctl >/dev/null 2>&1; then
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
local level_filter=""
|
||||
if [[ "$level" != "-1" ]]; then
|
||||
level_filter="-p $level"
|
||||
fi
|
||||
|
||||
journalctl -q --no-pager $level_filter _COMM=conmon _PID="$pid" 2>/dev/null || echo ""
|
||||
}
|
||||
|
||||
# Create a temporary directory for test
|
||||
setup_tmpdir() {
|
||||
export TEST_TMPDIR
|
||||
TEST_TMPDIR=$(mktemp -d /tmp/conmon-test-XXXXXX)
|
||||
}
|
||||
|
||||
# Cleanup temporary directory
|
||||
cleanup_tmpdir() {
|
||||
if [[ -n "$TEST_TMPDIR" ]]; then
|
||||
# Handle race condition where conmon might still be creating files
|
||||
local retries=5
|
||||
while [[ $retries -gt 0 ]]; do
|
||||
if rm -rf "$TEST_TMPDIR" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
((retries--))
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate OCI runtime configuration
|
||||
generate_runtime_config() {
|
||||
local bundle_path="$1"
|
||||
local rootfs="$2"
|
||||
local config_path="$bundle_path/config.json"
|
||||
|
||||
# Make rootfs path relative to bundle
|
||||
local relative_rootfs
|
||||
relative_rootfs=$(basename "$rootfs")
|
||||
|
||||
# Get current user UID and GID
|
||||
local host_uid host_gid
|
||||
host_uid=$(id -u)
|
||||
host_gid=$(id -g)
|
||||
|
||||
cat > "$config_path" << EOF
|
||||
{
|
||||
"ociVersion": "1.0.0",
|
||||
"process": {
|
||||
"terminal": false,
|
||||
"user": {
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
"args": [
|
||||
"/busybox",
|
||||
"echo",
|
||||
"busybox"
|
||||
],
|
||||
"env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
],
|
||||
"cwd": "/",
|
||||
"capabilities": {
|
||||
"bounding": [],
|
||||
"effective": [],
|
||||
"inheritable": [],
|
||||
"permitted": [],
|
||||
"ambient": []
|
||||
},
|
||||
"rlimits": [
|
||||
{
|
||||
"type": "RLIMIT_NOFILE",
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"noNewPrivileges": true
|
||||
},
|
||||
"root": {
|
||||
"path": "$relative_rootfs",
|
||||
"readonly": true
|
||||
},
|
||||
"hostname": "conmon-test",
|
||||
"mounts": [
|
||||
{
|
||||
"destination": "/proc",
|
||||
"type": "proc",
|
||||
"source": "proc"
|
||||
},
|
||||
{
|
||||
"destination": "/tmp",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": [
|
||||
"nosuid",
|
||||
"nodev",
|
||||
"mode=1777"
|
||||
]
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"resources": {
|
||||
"devices": [
|
||||
{
|
||||
"allow": false,
|
||||
"access": "rwm"
|
||||
}
|
||||
]
|
||||
},
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "pid"
|
||||
},
|
||||
{
|
||||
"type": "ipc"
|
||||
},
|
||||
{
|
||||
"type": "uts"
|
||||
},
|
||||
{
|
||||
"type": "mount"
|
||||
},
|
||||
{
|
||||
"type": "user"
|
||||
}
|
||||
],
|
||||
"uidMappings": [
|
||||
{
|
||||
"containerID": 0,
|
||||
"hostID": $host_uid,
|
||||
"size": 1
|
||||
}
|
||||
],
|
||||
"gidMappings": [
|
||||
{
|
||||
"containerID": 0,
|
||||
"hostID": $host_gid,
|
||||
"size": 1
|
||||
}
|
||||
],
|
||||
"maskedPaths": [
|
||||
"/proc/acpi",
|
||||
"/proc/kcore",
|
||||
"/proc/keys",
|
||||
"/proc/latency_stats",
|
||||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware"
|
||||
],
|
||||
"readonlyPaths": [
|
||||
"/proc/asound",
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Setup common test environment
|
||||
setup_test_env() {
|
||||
setup_tmpdir
|
||||
export CTR_ID
|
||||
CTR_ID=$(generate_ctr_id)
|
||||
export LOG_PATH="$TEST_TMPDIR/container.log"
|
||||
export PID_FILE="$TEST_TMPDIR/pidfile"
|
||||
export CONMON_PID_FILE="$TEST_TMPDIR/conmon-pidfile"
|
||||
export BUNDLE_PATH="$TEST_TMPDIR"
|
||||
export ROOTFS="$TEST_TMPDIR/rootfs"
|
||||
export SOCKET_PATH="$TEST_TMPDIR"
|
||||
}
|
||||
|
||||
# Setup full container environment with busybox
|
||||
setup_container_env() {
|
||||
setup_test_env
|
||||
|
||||
# Cache busybox binary for container tests
|
||||
cache_busybox
|
||||
|
||||
# Create the rootfs directory structure
|
||||
mkdir -p "$ROOTFS"/{bin,sbin,etc,proc,sys,dev,tmp}
|
||||
|
||||
# Copy busybox to rootfs and set up basic filesystem
|
||||
cp "$BUSYBOX_DEST" "$ROOTFS/busybox"
|
||||
chmod +x "$ROOTFS/busybox"
|
||||
|
||||
# Create busybox symlinks for common commands
|
||||
ln -sf busybox "$ROOTFS/bin/sh"
|
||||
ln -sf busybox "$ROOTFS/bin/echo"
|
||||
ln -sf busybox "$ROOTFS/bin/ls"
|
||||
ln -sf busybox "$ROOTFS/bin/cat"
|
||||
|
||||
# Create minimal /etc files
|
||||
echo "root:x:0:0:root:/:/bin/sh" > "$ROOTFS/etc/passwd"
|
||||
echo "root:x:0:" > "$ROOTFS/etc/group"
|
||||
|
||||
# Generate OCI runtime configuration
|
||||
generate_runtime_config "$BUNDLE_PATH" "$ROOTFS"
|
||||
}
|
||||
|
||||
# Cleanup test environment
|
||||
cleanup_test_env() {
|
||||
# Clean up any running containers
|
||||
if [[ -n "$CTR_ID" ]]; then
|
||||
"$RUNTIME_BINARY" delete -f "$CTR_ID" 2>/dev/null || true
|
||||
fi
|
||||
cleanup_tmpdir
|
||||
}
|
||||
|
||||
# Check if conmon binary exists and is executable
|
||||
check_conmon_binary() {
|
||||
if [[ ! -x "$CONMON_BINARY" ]]; then
|
||||
skip "conmon binary not found or not executable at $CONMON_BINARY"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if runtime binary exists and is executable
|
||||
check_runtime_binary() {
|
||||
if [[ ! -x "$RUNTIME_BINARY" ]]; then
|
||||
skip "runtime binary not found or not executable at $RUNTIME_BINARY"
|
||||
fi
|
||||
}
|
||||
|
||||
# Helper to check if a string contains a substring
|
||||
assert_output_contains() {
|
||||
local expected="$1"
|
||||
if [[ "$output" != *"$expected"* ]]; then
|
||||
echo "Expected output to contain: $expected"
|
||||
echo "Actual output: $output"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Helper to check if stderr contains a substring
|
||||
assert_stderr_contains() {
|
||||
local expected="$1"
|
||||
if [[ "$stderr" != *"$expected"* ]]; then
|
||||
echo "Expected stderr to contain: $expected"
|
||||
echo "Actual stderr: $stderr"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
Loading…
Reference in New Issue