go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/k8s/resolver_test.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package k8s
     5  
     6  import (
     7  	"context"
     8  	"encoding/base64"
     9  	"os"
    10  	"sort"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	"go.mondoo.com/cnquery/motor/asset"
    16  	"go.mondoo.com/cnquery/motor/providers"
    17  	"go.mondoo.com/cnquery/motor/providers/k8s"
    18  	"go.mondoo.com/cnquery/motor/providers/k8s/resources"
    19  )
    20  
    21  func TestManifestResolver(t *testing.T) {
    22  	resolver := &Resolver{}
    23  	manifestFile := "../../providers/k8s/resources/testdata/pod.yaml"
    24  
    25  	ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
    26  
    27  	assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
    28  		PlatformId: "//platform/k8s/uid/123/namespace/default/pods/name/mondoo",
    29  		Backend:    providers.ProviderType_K8S,
    30  		Options: map[string]string{
    31  			"path": manifestFile,
    32  		},
    33  		Discover: &providers.Discovery{
    34  			Targets: []string{"all"},
    35  		},
    36  	}, nil, nil)
    37  	require.NoError(t, err)
    38  	assert.Equal(t, 4, len(assetList))
    39  	assert.Equal(t, assetList[0].Platform.Name, "k8s-manifest")
    40  	assert.Equal(t, assetList[1].Platform.Name, "k8s-pod")
    41  	assert.Contains(t, assetList[1].Platform.Family, "k8s-workload")
    42  	assert.Contains(t, assetList[1].Platform.Family, "k8s")
    43  	assert.Equal(t, assetList[3].Platform.Runtime, "docker-registry")
    44  }
    45  
    46  func TestManifestResolver_NoNamespace(t *testing.T) {
    47  	resolver := &Resolver{}
    48  	manifestFile := "../../providers/k8s/resources/testdata/deployment-nons.yaml"
    49  
    50  	ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
    51  
    52  	assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
    53  		PlatformId: "//platform/k8s/uid/123/namespace/default/deployment/name/mondoo",
    54  		Backend:    providers.ProviderType_K8S,
    55  		Options: map[string]string{
    56  			"path": manifestFile,
    57  		},
    58  		Discover: &providers.Discovery{
    59  			Targets: []string{"all"},
    60  		},
    61  	}, nil, nil)
    62  	require.NoError(t, err)
    63  	assert.Equal(t, 2, len(assetList))
    64  	assert.Equal(t, assetList[0].Platform.Name, "k8s-manifest")
    65  	assert.Equal(t, assetList[1].Platform.Name, "k8s-deployment")
    66  	assert.Contains(t, assetList[1].Platform.Family, "k8s-workload")
    67  	assert.Contains(t, assetList[1].Platform.Family, "k8s")
    68  }
    69  
    70  func TestAdmissionReviewResolver(t *testing.T) {
    71  	resolver := &Resolver{}
    72  
    73  	ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
    74  	data, err := os.ReadFile("../../providers/k8s/resources/testdata/admission-review.json")
    75  	require.NoError(t, err)
    76  
    77  	admission := base64.StdEncoding.EncodeToString(data)
    78  	assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
    79  		Backend: providers.ProviderType_K8S,
    80  		Options: map[string]string{
    81  			k8s.OPTION_ADMISSION: admission,
    82  		},
    83  		Discover: &providers.Discovery{
    84  			Targets: []string{"all"},
    85  		},
    86  	}, nil, nil)
    87  	require.NoError(t, err)
    88  	assert.Equal(t, 4, len(assetList))
    89  	assert.Equal(t, assetList[0].Platform.Name, "kubernetes")
    90  	assert.Equal(t, assetList[1].Platform.Name, "k8s-pod")
    91  	assert.Contains(t, assetList[1].Platform.Family, "k8s-workload")
    92  	assert.Contains(t, assetList[1].Platform.Family, "k8s")
    93  	assert.Equal(t, assetList[2].Platform.Runtime, "docker-registry")
    94  	assert.Equal(t, assetList[3].Platform.Runtime, "k8s-admission")
    95  }
    96  
    97  func TestManifestResolverDiscoveries(t *testing.T) {
    98  	testCases := []struct {
    99  		kind               string
   100  		discoveryOption    string
   101  		platformName       string
   102  		expectedAssetNames []string
   103  	}{
   104  		{
   105  			kind:               "pod",
   106  			discoveryOption:    "pods",
   107  			platformName:       "k8s-pod",
   108  			expectedAssetNames: []string{"default/mondoo", "default/hello-pod-2"},
   109  		},
   110  		{
   111  			kind:               "cronjob",
   112  			discoveryOption:    "cronjobs",
   113  			platformName:       "k8s-cronjob",
   114  			expectedAssetNames: []string{"default/mondoo"},
   115  		},
   116  		{
   117  			kind:               "job",
   118  			discoveryOption:    "jobs",
   119  			platformName:       "k8s-job",
   120  			expectedAssetNames: []string{"default/mondoo"},
   121  		},
   122  		{
   123  			kind:               "statefulset",
   124  			discoveryOption:    "statefulsets",
   125  			platformName:       "k8s-statefulset",
   126  			expectedAssetNames: []string{"default/mondoo"},
   127  		},
   128  		{
   129  			kind:               "daemonset",
   130  			discoveryOption:    "daemonsets",
   131  			platformName:       "k8s-daemonset",
   132  			expectedAssetNames: []string{"default/mondoo"},
   133  		},
   134  		{
   135  			kind:               "replicaset",
   136  			discoveryOption:    "replicasets",
   137  			platformName:       "k8s-replicaset",
   138  			expectedAssetNames: []string{"default/mondoo"},
   139  		},
   140  		{
   141  			kind:               "deployment",
   142  			discoveryOption:    "deployments",
   143  			platformName:       "k8s-deployment",
   144  			expectedAssetNames: []string{"default/mondoo"},
   145  		},
   146  	}
   147  
   148  	for _, testCase := range testCases {
   149  		t.Run("discover k8s "+testCase.kind, func(t *testing.T) {
   150  			resolver := &Resolver{}
   151  			manifestFile := "../../providers/k8s/resources/testdata/" + testCase.kind + ".yaml"
   152  
   153  			ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
   154  
   155  			assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
   156  				PlatformId: "//platform/k8s/uid/123/namespace/default/" + testCase.discoveryOption + "/name/mondoo",
   157  				Backend:    providers.ProviderType_K8S,
   158  				Options: map[string]string{
   159  					"path": manifestFile,
   160  				},
   161  				Discover: &providers.Discovery{
   162  					Targets: []string{testCase.discoveryOption},
   163  				},
   164  			}, nil, nil)
   165  			require.NoError(t, err)
   166  			// When this check fails locally, check your kubeconfig.
   167  			// context has to reference the default namespace
   168  			assert.Equal(t, len(testCase.expectedAssetNames), len(assetList))
   169  
   170  			for _, a := range assetList {
   171  				assert.Contains(t, a.Platform.Family, "k8s-workload")
   172  				assert.Contains(t, a.Platform.Family, "k8s")
   173  				assert.Equal(t, "k8s-manifest", a.Platform.Runtime)
   174  				assert.Equal(t, testCase.platformName, a.Platform.Name)
   175  				assert.Contains(t, testCase.expectedAssetNames, a.Name)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  func TestManifestResolverMultiPodDiscovery(t *testing.T) {
   182  	resolver := &Resolver{}
   183  	manifestFile := "../../providers/k8s/resources/testdata/pod.yaml"
   184  
   185  	ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
   186  
   187  	assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
   188  		PlatformId: "//platform/k8s/uid/123/namespace/default/pods/name/mondoo",
   189  		Backend:    providers.ProviderType_K8S,
   190  		Options: map[string]string{
   191  			"path": manifestFile,
   192  		},
   193  		Discover: &providers.Discovery{
   194  			Targets: []string{"pods"},
   195  		},
   196  	}, nil, nil)
   197  	require.NoError(t, err)
   198  	// When this check fails locally, check your kubeconfig.
   199  	// context has to reference the default namespace
   200  	assert.Equal(t, 2, len(assetList))
   201  	sort.Slice(assetList, func(i, j int) bool {
   202  		return assetList[i].Name < assetList[j].Name
   203  	})
   204  	assert.Contains(t, assetList[0].Platform.Family, "k8s-workload")
   205  	assert.Contains(t, assetList[0].Platform.Family, "k8s")
   206  	assert.Equal(t, "k8s-manifest", assetList[0].Platform.Runtime)
   207  	assert.Equal(t, "k8s-pod", assetList[0].Platform.Name)
   208  	assert.Equal(t, "default/hello-pod-2", assetList[0].Name)
   209  	assert.Contains(t, assetList[1].Platform.Family, "k8s-workload")
   210  	assert.Contains(t, assetList[1].Platform.Family, "k8s")
   211  	assert.Equal(t, "k8s-manifest", assetList[1].Platform.Runtime)
   212  	assert.Equal(t, "k8s-pod", assetList[1].Platform.Name)
   213  	assert.Equal(t, "default/mondoo", assetList[1].Name)
   214  }
   215  
   216  func TestManifestResolverWrongDiscovery(t *testing.T) {
   217  	resolver := &Resolver{}
   218  	manifestFile := "../../providers/k8s/resources/testdata/cronjob.yaml"
   219  
   220  	ctx := resources.SetDiscoveryCache(context.Background(), resources.NewDiscoveryCache())
   221  
   222  	assetList, err := resolver.Resolve(ctx, &asset.Asset{}, &providers.Config{
   223  		Backend: providers.ProviderType_K8S,
   224  		Options: map[string]string{
   225  			"path":      manifestFile,
   226  			"namespace": "default",
   227  		},
   228  		Discover: &providers.Discovery{
   229  			Targets: []string{"pods"},
   230  		},
   231  	}, nil, nil)
   232  	require.NoError(t, err)
   233  	// When this check fails locally, check your kubeconfig.
   234  	// context has to reference the default namespace
   235  	assert.Equalf(t, 0, len(assetList), "discovering pods in a cronjob manifest should result in no assets")
   236  }
   237  
   238  func TestResourceFilter(t *testing.T) {
   239  	cfg := &providers.Config{
   240  		Backend: providers.ProviderType_K8S,
   241  		Options: map[string]string{
   242  			"k8s-resources": "pod:default:nginx, pod:default:redis, deployment:test:redis, node:node1",
   243  		},
   244  	}
   245  
   246  	resFilters, err := resourceFilters(cfg)
   247  	require.NoError(t, err)
   248  
   249  	expected := map[string][]K8sResourceIdentifier{
   250  		"pod": {
   251  			{Type: "pod", Namespace: "default", Name: "nginx"},
   252  			{Type: "pod", Namespace: "default", Name: "redis"},
   253  		},
   254  		"deployment": {
   255  			{Type: "deployment", Namespace: "test", Name: "redis"},
   256  		},
   257  		"node": {
   258  			{Type: "node", Namespace: "", Name: "node1"},
   259  		},
   260  	}
   261  
   262  	assert.Equal(t, expected, resFilters)
   263  }