mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
This change moves the `system.SecurityOpt` type and `system.DecodeSecurityOptions` function to the client and adds a set of unit tests to capture current implementation. This change also create a set of daemon backend copies for usage. Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
241 lines
4.8 KiB
Go
241 lines
4.8 KiB
Go
package security
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"gotest.tools/v3/assert"
|
|
"gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func TestDecode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
opts []string
|
|
want []Option
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "empty options",
|
|
opts: []string{},
|
|
want: []Option{},
|
|
},
|
|
{
|
|
name: "nil options",
|
|
opts: nil,
|
|
want: []Option{},
|
|
},
|
|
{
|
|
name: "legacy format without equals",
|
|
opts: []string{"apparmor:unconfined"},
|
|
want: []Option{
|
|
{Name: "apparmor:unconfined"},
|
|
},
|
|
},
|
|
{
|
|
name: "single option with name only",
|
|
opts: []string{"name=apparmor"},
|
|
want: []Option{
|
|
{Name: "apparmor"},
|
|
},
|
|
},
|
|
{
|
|
name: "single option with name and additional options",
|
|
opts: []string{"name=selinux,type=container_t,level=s0:c1.c2"},
|
|
want: []Option{
|
|
{
|
|
Name: "selinux",
|
|
Options: []KeyValue{
|
|
{Key: "type", Value: "container_t"},
|
|
{Key: "level", Value: "s0:c1.c2"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple options",
|
|
opts: []string{
|
|
"name=apparmor,profile=docker-default",
|
|
"name=seccomp,profile=unconfined",
|
|
},
|
|
want: []Option{
|
|
{
|
|
Name: "apparmor",
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "docker-default"},
|
|
},
|
|
},
|
|
{
|
|
Name: "seccomp",
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "unconfined"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "mixed legacy and new format",
|
|
opts: []string{
|
|
"label:disable",
|
|
"name=apparmor,profile=custom",
|
|
},
|
|
want: []Option{
|
|
{Name: "label:disable"},
|
|
{
|
|
Name: "apparmor",
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "custom"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "option without name key",
|
|
opts: []string{"profile=custom,type=container_t"},
|
|
want: []Option{
|
|
{
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "custom"},
|
|
{Key: "type", Value: "container_t"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "option with equals in value",
|
|
opts: []string{"name=selinux,level=s0:c1=c2"},
|
|
want: []Option{
|
|
{
|
|
Name: "selinux",
|
|
Options: []KeyValue{
|
|
{Key: "level", Value: "s0:c1=c2"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid option without equals in comma-separated list",
|
|
opts: []string{"name=apparmor,invalid"},
|
|
wantErr: `invalid security option "invalid"`,
|
|
},
|
|
{
|
|
name: "empty key",
|
|
opts: []string{"=value"},
|
|
wantErr: "invalid empty security option",
|
|
},
|
|
{
|
|
name: "empty value",
|
|
opts: []string{"key="},
|
|
wantErr: "invalid empty security option",
|
|
},
|
|
{
|
|
name: "empty key and value",
|
|
opts: []string{"="},
|
|
wantErr: "invalid empty security option",
|
|
},
|
|
{
|
|
name: "empty key in middle",
|
|
opts: []string{"name=apparmor,=value"},
|
|
wantErr: "invalid empty security option",
|
|
},
|
|
{
|
|
name: "empty value in middle",
|
|
opts: []string{"name=apparmor,key="},
|
|
wantErr: "invalid empty security option",
|
|
},
|
|
{
|
|
name: "complex real-world example",
|
|
opts: []string{
|
|
"name=selinux,user=system_u,role=system_r,type=container_t,level=s0:c1.c2",
|
|
"name=apparmor,profile=/usr/bin/docker",
|
|
"name=seccomp,profile=builtin",
|
|
},
|
|
want: []Option{
|
|
{
|
|
Name: "selinux",
|
|
Options: []KeyValue{
|
|
{Key: "user", Value: "system_u"},
|
|
{Key: "role", Value: "system_r"},
|
|
{Key: "type", Value: "container_t"},
|
|
{Key: "level", Value: "s0:c1.c2"},
|
|
},
|
|
},
|
|
{
|
|
Name: "apparmor",
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "/usr/bin/docker"},
|
|
},
|
|
},
|
|
{
|
|
Name: "seccomp",
|
|
Options: []KeyValue{
|
|
{Key: "profile", Value: "builtin"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, err := DecodeOptions(tc.opts)
|
|
|
|
if tc.wantErr == "" {
|
|
assert.NilError(t, err)
|
|
assert.Check(t, cmp.DeepEqual(got, tc.want))
|
|
} else {
|
|
assert.Check(t, err != nil, "expected error but got none")
|
|
assert.ErrorContains(t, err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecode(b *testing.B) {
|
|
opts := []string{
|
|
"name=selinux,user=system_u,role=system_r,type=container_t,level=s0:c1.c2",
|
|
"name=apparmor,profile=/usr/bin/docker",
|
|
"name=seccomp,profile=builtin",
|
|
"legacy:format",
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := DecodeOptions(opts)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeLegacy(b *testing.B) {
|
|
opts := []string{
|
|
"apparmor:unconfined",
|
|
"label:disable",
|
|
"seccomp:unconfined",
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := DecodeOptions(opts)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeComplex(b *testing.B) {
|
|
opts := make([]string, 100)
|
|
for i := range opts {
|
|
opts[i] = fmt.Sprintf("name=test%d,key1=value1,key2=value2,key3=value3", i)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := DecodeOptions(opts)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|