Skip to content

Commit

Permalink
feat: add detection for cilium
Browse files Browse the repository at this point in the history
  • Loading branch information
joshiste committed Oct 17, 2024
1 parent 0b393b3 commit 4c9e64a
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 49 deletions.
72 changes: 32 additions & 40 deletions go/action_kit_commons/network/list_interfaces.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 Steadybit GmbH
// SPDX-FileCopyrightText: 2024 Steadybit GmbH

package network

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/steadybit/action-kit/go/action_kit_commons/runc"
"strings"
)

type Interface struct {
Expand All @@ -32,6 +29,16 @@ type AddrInfo struct {
Broadcast string `json:"broadcast"`
}

type Route struct {
Dst string `json:"dst"`
Gateway string `json:"gateway"`
Dev string `json:"dev"`
Flags []string `json:"flags"`
Protocol string `json:"protocol"`
Prefsrc string `json:"prefsrc"`
Scope string `json:"scope"`
}

func (i *Interface) HasFlag(f string) bool {
for _, flag := range i.Flags {
if flag == f {
Expand Down Expand Up @@ -61,50 +68,35 @@ func ListNonLoopbackInterfaceNames(ctx context.Context, r runc.Runc, sidecar Sid
return ifcNames, nil
}

func ListInterfaces(ctx context.Context, r runc.Runc, sidecar SidecarOpts) ([]Interface, error) {
id := getNextContainerId("ip-addr", sidecar.IdSuffix)

bundle, err := r.Create(ctx, "/", id)
func HasCiliumIpRoutes(ctx context.Context, r runc.Runc, sidecar SidecarOpts) (bool, error) {
out, err := executeIpCommands(ctx, r, sidecar, []string{"route list"}, "--json")
if err != nil {
return nil, err
return false, err
}
defer func() {
if err := bundle.Remove(); err != nil {
log.Warn().Str("id", id).Err(err).Msg("failed to remove bundle")
}
}()

runc.RefreshNamespaces(ctx, sidecar.TargetProcess.Namespaces, specs.NetworkNamespace)

if err = bundle.EditSpec(
runc.WithHostname(fmt.Sprintf("ip-link-show-%s", id)),
runc.WithAnnotations(map[string]string{
"com.steadybit.sidecar": "true",
}),
runc.WithNamespaces(runc.FilterNamespaces(sidecar.TargetProcess.Namespaces, specs.NetworkNamespace)),
runc.WithCapabilities("CAP_NET_ADMIN"),
runc.WithProcessArgs("ip", "--json", "address", "show", "up")); err != nil {
return nil, err

var rules []Route
if err = json.Unmarshal([]byte(out), &rules); err != nil {
return false, fmt.Errorf("failed to unmarshal rules: %w", err)
}

var outb, errb bytes.Buffer
err = r.Run(ctx, bundle, runc.IoOpts{Stdout: &outb, Stderr: &errb})
defer func() {
if err := r.Delete(context.Background(), id, true); err != nil {
level := zerolog.WarnLevel
if errors.Is(err, runc.ErrContainerNotFound) {
level = zerolog.DebugLevel
}
log.WithLevel(level).Str("id", id).Err(err).Msg("failed to delete container")
log.Trace().Interface("rules", rules).Msg("listed routes")

for _, rule := range rules {
if strings.HasPrefix(rule.Dev, "cilium_") {
return true, nil
}
}()
}
return false, nil
}

func ListInterfaces(ctx context.Context, r runc.Runc, sidecar SidecarOpts) ([]Interface, error) {
out, err := executeIpCommands(ctx, r, sidecar, []string{"address show up"}, "--json")
if err != nil {
return nil, fmt.Errorf("failed to list interfaces: %w: %s", err, errb.String())
return nil, err
}

var interfaces []Interface
err = json.Unmarshal(outb.Bytes(), &interfaces)
if err != nil {
if err = json.Unmarshal([]byte(out), &interfaces); err != nil {
return nil, fmt.Errorf("failed to unmarshal interfaces: %w", err)
}

Expand Down
56 changes: 56 additions & 0 deletions go/action_kit_commons/network/list_interfaces_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2024 Steadybit GmbH

package network

import (
"context"
"github.com/steadybit/action-kit/go/action_kit_commons/runc"
"github.com/stretchr/testify/assert"
"testing"
)

func TestHasCiliumIpRoutes(t *testing.T) {
defer func() {
executeIpCommands = executeIpCommandsImpl
}()

tests := []struct {
name string
stdout string
want bool
wantErr assert.ErrorAssertionFunc
}{
{
name: "no routes",
stdout: "[]",
want: false,
wantErr: assert.NoError,
},
{
name: "cilium",
stdout: "[{\"dst\":\"default\",\"gateway\":\"192.168.58.1\",\"dev\":\"eth0\",\"flags\":[]},{\"dst\":\"10.0.0.0/24\",\"gateway\":\"10.0.0.24\",\"dev\":\"cilium_host\",\"protocol\":\"kernel\",\"prefsrc\":\"10.0.0.24\",\"flags\":[]},{\"dst\":\"10.0.0.24\",\"dev\":\"cilium_host\",\"protocol\":\"kernel\",\"scope\":\"link\",\"flags\":[]},{\"dst\":\"10.244.0.0/16\",\"dev\":\"bridge\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"10.244.0.1\",\"flags\":[]},{\"dst\":\"172.17.0.0/16\",\"dev\":\"docker0\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"172.17.0.1\",\"flags\":[\"linkdown\"]},{\"dst\":\"192.168.58.0/24\",\"dev\":\"eth0\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"192.168.58.2\",\"flags\":[]}]",
want: true,
wantErr: assert.NoError,
},
{
name: "no cilium",
stdout: "[{\"dst\":\"default\",\"gateway\":\"192.168.58.1\",\"dev\":\"eth0\",\"flags\":[]},{\"dst\":\"10.244.0.0/16\",\"dev\":\"bridge\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"10.244.0.1\",\"flags\":[]},{\"dst\":\"172.17.0.0/16\",\"dev\":\"docker0\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"172.17.0.1\",\"flags\":[\"linkdown\"]},{\"dst\":\"192.168.58.0/24\",\"dev\":\"eth0\",\"protocol\":\"kernel\",\"scope\":\"link\",\"prefsrc\":\"192.168.58.2\",\"flags\":[]}]",
want: false,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
executeIpCommands = func(ctx context.Context, r runc.Runc, sidecar SidecarOpts, cmds []string, extraArgs ...string) (string, error) {
return tt.stdout, nil
}

got, err := HasCiliumIpRoutes(context.Background(), nil, SidecarOpts{})
if !tt.wantErr(t, err, "HasCiliumIpRoutes()") {
return
}
assert.Equalf(t, tt.want, got, "HasCiliumIpRoutes()")
})
}
}
14 changes: 8 additions & 6 deletions go/action_kit_commons/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ func generateAndRunCommands(ctx context.Context, r runc.Runc, sidecar SidecarOpt
}

if len(ipCommandsV4) > 0 {
if _, ipErr := executeIpCommands(ctx, r, sidecar, FamilyV4, ipCommandsV4); ipErr != nil {
if _, ipErr := executeIpCommands(ctx, r, sidecar, ipCommandsV4, "-family", string(FamilyV4)); ipErr != nil {
err = errors.Join(err, FilterBatchErrors(ipErr, mode, ipCommandsV4))
}
}

if len(ipCommandsV6) > 0 {
if _, ipErr := executeIpCommands(ctx, r, sidecar, FamilyV6, ipCommandsV6); ipErr != nil {
if _, ipErr := executeIpCommands(ctx, r, sidecar, ipCommandsV6, "-family", string(FamilyV6)); ipErr != nil {
err = errors.Join(err, FilterBatchErrors(ipErr, mode, ipCommandsV6))
}
}
Expand Down Expand Up @@ -133,7 +133,7 @@ func logCurrentIpRules(ctx context.Context, r runc.Runc, sidecar SidecarOpts, fa
return
}

stdout, err := executeIpCommands(ctx, r, sidecar, family, []string{"rule show"})
stdout, err := executeIpCommands(ctx, r, sidecar, []string{"rule show"}, "-family", string(family))
if err != nil {
log.Trace().Err(err).Msg("failed to get current ip rules")
return
Expand Down Expand Up @@ -190,7 +190,9 @@ func defaultIpv6Supported() bool {
return true
}

func executeIpCommands(ctx context.Context, r runc.Runc, sidecar SidecarOpts, family Family, cmds []string) (string, error) {
var executeIpCommands = executeIpCommandsImpl

func executeIpCommandsImpl(ctx context.Context, r runc.Runc, sidecar SidecarOpts, cmds []string, extraArgs ...string) (string, error) {
if len(cmds) == 0 {
return "", nil
}
Expand All @@ -208,7 +210,7 @@ func executeIpCommands(ctx context.Context, r runc.Runc, sidecar SidecarOpts, fa

runc.RefreshNamespaces(ctx, sidecar.TargetProcess.Namespaces, specs.NetworkNamespace)

processArgs := []string{"ip", "-family", string(family), "-force", "-batch", "-"}
processArgs := append([]string{"ip", "-force", "-batch", "-"}, extraArgs...)
if err = bundle.EditSpec(
runc.WithHostname(fmt.Sprintf("ip-%s", id)),
runc.WithAnnotations(map[string]string{"com.steadybit.sidecar": "true"}),
Expand All @@ -219,7 +221,7 @@ func executeIpCommands(ctx context.Context, r runc.Runc, sidecar SidecarOpts, fa
return "", err
}

log.Debug().Strs("cmds", cmds).Str("family", string(family)).Msg("running ip commands")
log.Debug().Strs("cmds", cmds).Strs("extraArgs", extraArgs).Msg("running ip commands")
var outb bytes.Buffer
err = r.Run(ctx, bundle, runc.IoOpts{
Stdin: ToReader(cmds),
Expand Down
7 changes: 4 additions & 3 deletions go/action_kit_sdk/action_http_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/steadybit/action-kit/go/action_kit_api/v2"
"github.com/steadybit/action-kit/go/action_kit_sdk/state_persister"
Expand Down Expand Up @@ -78,9 +79,9 @@ func (a *actionHttpAdapter[T]) handlePrepare(w http.ResponseWriter, r *http.Requ
state := a.action.NewEmptyState()
result, err := a.action.Prepare(r.Context(), &state, *prepareActionRequestBody)
if err != nil {
extensionError, isExtensionError := err.(extension_kit.ExtensionError)
if isExtensionError {
exthttp.WriteError(w, extensionError)
var extErr *extension_kit.ExtensionError
if errors.As(err, &extErr) {
exthttp.WriteError(w, *extErr)
} else {
exthttp.WriteError(w, extension_kit.ToError("Failed to prepare.", err))
}
Expand Down
1 change: 1 addition & 0 deletions go/action_kit_sdk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22
require (
github.com/google/uuid v1.6.0
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.33.0
github.com/steadybit/action-kit/go/action_kit_api/v2 v2.9.2
github.com/steadybit/extension-kit v1.8.15
Expand Down
1 change: 1 addition & 0 deletions go/action_kit_sdk/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down

0 comments on commit 4c9e64a

Please sign in to comment.