github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/hooks/exec/runtimeconfigfilter_test.go (about)

     1  package exec
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	spec "github.com/opencontainers/runtime-spec/specs-go"
    11  	"github.com/pkg/errors"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func pointerInt(value int) *int {
    16  	return &value
    17  }
    18  
    19  func pointerUInt32(value uint32) *uint32 {
    20  	return &value
    21  }
    22  
    23  func pointerFileMode(value os.FileMode) *os.FileMode {
    24  	return &value
    25  }
    26  
    27  func TestRuntimeConfigFilter(t *testing.T) {
    28  	unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint
    29  
    30  	for _, tt := range []struct {
    31  		name              string
    32  		contextTimeout    time.Duration
    33  		hooks             []spec.Hook
    34  		input             *spec.Spec
    35  		expected          *spec.Spec
    36  		expectedHookError string
    37  		expectedRunError  error
    38  	}{
    39  		{
    40  			name: "no-op",
    41  			hooks: []spec.Hook{
    42  				{
    43  					Path: path,
    44  					Args: []string{"sh", "-c", "cat"},
    45  				},
    46  			},
    47  			input: &spec.Spec{
    48  				Version: "1.0.0",
    49  				Root: &spec.Root{
    50  					Path: "rootfs",
    51  				},
    52  			},
    53  			expected: &spec.Spec{
    54  				Version: "1.0.0",
    55  				Root: &spec.Root{
    56  					Path: "rootfs",
    57  				},
    58  			},
    59  		},
    60  		{
    61  			name: "device injection",
    62  			hooks: []spec.Hook{
    63  				{
    64  					Path: path,
    65  					Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
    66  				},
    67  			},
    68  			input: &spec.Spec{
    69  				Version: "1.0.0",
    70  				Root: &spec.Root{
    71  					Path: "rootfs",
    72  				},
    73  				Linux: &spec.Linux{
    74  					Devices: []spec.LinuxDevice{
    75  						{
    76  							Path:     "/dev/fuse",
    77  							Type:     "c",
    78  							Major:    10,
    79  							Minor:    229,
    80  							FileMode: pointerFileMode(0600),
    81  							UID:      pointerUInt32(0),
    82  							GID:      pointerUInt32(0),
    83  						},
    84  					},
    85  				},
    86  			},
    87  			expected: &spec.Spec{
    88  				Version: "1.0.0",
    89  				Root: &spec.Root{
    90  					Path: "rootfs",
    91  				},
    92  				Linux: &spec.Linux{
    93  					Devices: []spec.LinuxDevice{
    94  						{
    95  							Path:     "/dev/fuse",
    96  							Type:     "c",
    97  							Major:    10,
    98  							Minor:    229,
    99  							FileMode: pointerFileMode(0600),
   100  							UID:      pointerUInt32(0),
   101  							GID:      pointerUInt32(0),
   102  						},
   103  						{
   104  							Path:     "/dev/sda",
   105  							Type:     "b",
   106  							Major:    8,
   107  							Minor:    0,
   108  							FileMode: pointerFileMode(0600),
   109  							UID:      pointerUInt32(0),
   110  							GID:      pointerUInt32(0),
   111  						},
   112  					},
   113  				},
   114  			},
   115  		},
   116  		{
   117  			name: "chaining",
   118  			hooks: []spec.Hook{
   119  				{
   120  					Path: path,
   121  					Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
   122  				},
   123  				{
   124  					Path: path,
   125  					Args: []string{"sh", "-c", `sed 's|/dev/sda|/dev/sdb|'`},
   126  				},
   127  			},
   128  			input: &spec.Spec{
   129  				Version: "1.0.0",
   130  				Root: &spec.Root{
   131  					Path: "rootfs",
   132  				},
   133  				Linux: &spec.Linux{
   134  					Devices: []spec.LinuxDevice{
   135  						{
   136  							Path:     "/dev/fuse",
   137  							Type:     "c",
   138  							Major:    10,
   139  							Minor:    229,
   140  							FileMode: pointerFileMode(0600),
   141  							UID:      pointerUInt32(0),
   142  							GID:      pointerUInt32(0),
   143  						},
   144  					},
   145  				},
   146  			},
   147  			expected: &spec.Spec{
   148  				Version: "1.0.0",
   149  				Root: &spec.Root{
   150  					Path: "rootfs",
   151  				},
   152  				Linux: &spec.Linux{
   153  					Devices: []spec.LinuxDevice{
   154  						{
   155  							Path:     "/dev/fuse",
   156  							Type:     "c",
   157  							Major:    10,
   158  							Minor:    229,
   159  							FileMode: pointerFileMode(0600),
   160  							UID:      pointerUInt32(0),
   161  							GID:      pointerUInt32(0),
   162  						},
   163  						{
   164  							Path:     "/dev/sdb",
   165  							Type:     "b",
   166  							Major:    8,
   167  							Minor:    0,
   168  							FileMode: pointerFileMode(0600),
   169  							UID:      pointerUInt32(0),
   170  							GID:      pointerUInt32(0),
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  		{
   177  			name:           "context timeout",
   178  			contextTimeout: time.Duration(1) * time.Second,
   179  			hooks: []spec.Hook{
   180  				{
   181  					Path: path,
   182  					Args: []string{"sh", "-c", "sleep 2"},
   183  				},
   184  			},
   185  			input: &spec.Spec{
   186  				Version: "1.0.0",
   187  				Root: &spec.Root{
   188  					Path: "rootfs",
   189  				},
   190  			},
   191  			expected: &spec.Spec{
   192  				Version: "1.0.0",
   193  				Root: &spec.Root{
   194  					Path: "rootfs",
   195  				},
   196  			},
   197  			expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
   198  			expectedRunError:  context.DeadlineExceeded,
   199  		},
   200  		{
   201  			name: "hook timeout",
   202  			hooks: []spec.Hook{
   203  				{
   204  					Path:    path,
   205  					Args:    []string{"sh", "-c", "sleep 2"},
   206  					Timeout: pointerInt(1),
   207  				},
   208  			},
   209  			input: &spec.Spec{
   210  				Version: "1.0.0",
   211  				Root: &spec.Root{
   212  					Path: "rootfs",
   213  				},
   214  			},
   215  			expected: &spec.Spec{
   216  				Version: "1.0.0",
   217  				Root: &spec.Root{
   218  					Path: "rootfs",
   219  				},
   220  			},
   221  			expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
   222  			expectedRunError:  context.DeadlineExceeded,
   223  		},
   224  		{
   225  			name: "invalid JSON",
   226  			hooks: []spec.Hook{
   227  				{
   228  					Path: path,
   229  					Args: []string{"sh", "-c", "echo '{'"},
   230  				},
   231  			},
   232  			input: &spec.Spec{
   233  				Version: "1.0.0",
   234  				Root: &spec.Root{
   235  					Path: "rootfs",
   236  				},
   237  			},
   238  			expected: &spec.Spec{
   239  				Version: "1.0.0",
   240  				Root: &spec.Root{
   241  					Path: "rootfs",
   242  				},
   243  			},
   244  			expectedRunError: unexpectedEndOfJSONInput,
   245  		},
   246  	} {
   247  		test := tt
   248  		t.Run(test.name, func(t *testing.T) {
   249  			ctx := context.Background()
   250  			if test.contextTimeout > 0 {
   251  				var cancel context.CancelFunc
   252  				ctx, cancel = context.WithTimeout(ctx, test.contextTimeout)
   253  				defer cancel()
   254  			}
   255  			hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout)
   256  			assert.Equal(t, test.expectedRunError, errors.Cause(err))
   257  			if test.expectedHookError == "" {
   258  				if hookErr != nil {
   259  					t.Fatal(hookErr)
   260  				}
   261  			} else {
   262  				assert.Regexp(t, test.expectedHookError, hookErr.Error())
   263  			}
   264  			assert.Equal(t, test.expected, test.input)
   265  		})
   266  	}
   267  }