github.com/GoogleCloudPlatform/compute-image-tools/cli_tools@v0.0.0-20240516224744-de2dabc4ed1b/common/imagefile/inspector_test.go (about)

     1  //  Copyright 2020 Google Inc. All Rights Reserved.
     2  //
     3  //  Licensed under the Apache License, Version 2.0 (the "License");
     4  //  you may not use this file except in compliance with the License.
     5  //  You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //  Unless required by applicable law or agreed to in writing, software
    10  //  distributed under the License is distributed on an "AS IS" BASIS,
    11  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //  See the License for the specific language governing permissions and
    13  //  limitations under the License.
    14  
    15  package imagefile
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"io/ioutil"
    21  	"path"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  const (
    28  	mountError      = "mount failure"
    29  	inspectionError = "inspection failure"
    30  )
    31  
    32  func TestGCSInspector_RoundBytesUp(t *testing.T) {
    33  	cases := []struct {
    34  		name                                string
    35  		actualSizeBytes, virtualSizeBytes   int64
    36  		expectedActualGB, expectedVirtualGB int64
    37  	}{
    38  		{
    39  			name: "zero doesn't round up",
    40  		},
    41  
    42  		{
    43  			name:              "whole doesn't round up",
    44  			actualSizeBytes:   bytesPerGB * 10,
    45  			expectedActualGB:  10,
    46  			virtualSizeBytes:  bytesPerGB * 8,
    47  			expectedVirtualGB: 8,
    48  		},
    49  
    50  		{
    51  			name:              "plus one rounds up",
    52  			actualSizeBytes:   bytesPerGB + 1,
    53  			expectedActualGB:  2,
    54  			virtualSizeBytes:  (bytesPerGB * 2) + 1,
    55  			expectedVirtualGB: 3,
    56  		},
    57  
    58  		{
    59  			name:              "minus one rounds up",
    60  			actualSizeBytes:   bytesPerGB - 1,
    61  			expectedActualGB:  1,
    62  			virtualSizeBytes:  (bytesPerGB * 2) - 1,
    63  			expectedVirtualGB: 2,
    64  		},
    65  	}
    66  
    67  	for _, tt := range cases {
    68  		t.Run(tt.name, func(t *testing.T) {
    69  			gcsURI, client := setupClient(t, 0, 0, ImageInfo{
    70  				Format:           "vmdk",
    71  				ActualSizeBytes:  tt.actualSizeBytes,
    72  				VirtualSizeBytes: tt.virtualSizeBytes,
    73  			})
    74  			ctx := context.Background()
    75  			result, err := client.Inspect(ctx, gcsURI)
    76  			assert.NoError(t, err)
    77  			assert.Equal(t, tt.expectedActualGB, result.PhysicalSizeGB)
    78  			assert.Equal(t, tt.expectedVirtualGB, result.VirtualSizeGB)
    79  		})
    80  	}
    81  }
    82  
    83  func TestGCSInspector_DontRetryMount_IfContextCancelled(t *testing.T) {
    84  	mountFailures := 1
    85  	gcsURI, client := setupClient(t, mountFailures, 0, ImageInfo{
    86  		Format:           "vmdk",
    87  		ActualSizeBytes:  1024,
    88  		VirtualSizeBytes: 1024,
    89  	})
    90  	ctx, cancel := context.WithCancel(context.Background())
    91  	cancel()
    92  	_, err := client.Inspect(ctx, gcsURI)
    93  	assert.EqualError(t, err, mountError)
    94  }
    95  
    96  func TestGCSInspector_DontRetryInspect_IfContextCancelled(t *testing.T) {
    97  	inspectFailures := 1
    98  	gcsURI, client := setupClient(t, 0, inspectFailures, ImageInfo{
    99  		Format:           "vmdk",
   100  		ActualSizeBytes:  1024,
   101  		VirtualSizeBytes: 1024,
   102  	})
   103  	ctx, cancel := context.WithCancel(context.Background())
   104  	cancel()
   105  	_, err := client.Inspect(ctx, gcsURI)
   106  	assert.EqualError(t, err, inspectionError)
   107  }
   108  
   109  func TestGCSInspector_PerformRetry_WhenMountingFails(t *testing.T) {
   110  	// Fail mounting three times, and then successfully mount.
   111  	mountFailures := 3
   112  	gcsURI, client := setupClient(t, mountFailures, 0, ImageInfo{
   113  		Format:           "vmdk",
   114  		ActualSizeBytes:  1024,
   115  		VirtualSizeBytes: 1024,
   116  	})
   117  	_, err := client.Inspect(context.Background(), gcsURI)
   118  	assert.NoError(t, err)
   119  }
   120  
   121  func TestGCSInspector_PerformRetry_WhenInspectionFails(t *testing.T) {
   122  	// Fail inspection three times, and then successfully inspect.
   123  	inspectFailures := 3
   124  	gcsURI, client := setupClient(t, 0, inspectFailures, ImageInfo{
   125  		Format:           "vmdk",
   126  		ActualSizeBytes:  1024,
   127  		VirtualSizeBytes: 1024,
   128  	})
   129  	_, err := client.Inspect(context.Background(), gcsURI)
   130  	assert.NoError(t, err)
   131  }
   132  
   133  func setupClient(t *testing.T, mountFailures, inspectFailures int, qemuResult ImageInfo) (
   134  	string, Inspector) {
   135  	pathToFakeMount, err := ioutil.TempFile("", "")
   136  	assert.NoError(t, err)
   137  	defer pathToFakeMount.Close()
   138  	assert.NoError(t, err)
   139  	objectName := path.Base(pathToFakeMount.Name())
   140  	fakeMountDir := path.Dir(pathToFakeMount.Name())
   141  	gcsURI := "gs://bucket/" + objectName
   142  
   143  	inspector := NewGCSInspector().(gcsInspector)
   144  	inspector.fuseClient = &mockGCSFuse{
   145  		failuresRemaining: mountFailures,
   146  		expectedBucket:    "bucket",
   147  		t:                 t,
   148  		returnValue:       fakeMountDir,
   149  	}
   150  	inspector.qemuClient = &mockQemuClient{
   151  		failuresRemaining: inspectFailures,
   152  		expectedFilename:  pathToFakeMount.Name(),
   153  		t:                 t,
   154  		returnValue:       qemuResult,
   155  	}
   156  	return gcsURI, inspector
   157  }
   158  
   159  type mockGCSFuse struct {
   160  	failuresRemaining int
   161  	expectedBucket    string
   162  	t                 *testing.T
   163  	returnValue       string
   164  }
   165  
   166  func (m *mockGCSFuse) MountToTemp(ctx context.Context, bucket string) (string, error) {
   167  	assert.Equal(m.t, m.expectedBucket, bucket)
   168  	if m.failuresRemaining > 0 {
   169  		m.failuresRemaining--
   170  		err := errors.New(mountError)
   171  		m.t.Logf("gcsfuse returning %v", err)
   172  		return "", err
   173  	}
   174  	return m.returnValue, nil
   175  }
   176  
   177  func (m *mockGCSFuse) Unmount(directory string) error {
   178  	return nil
   179  }
   180  
   181  type mockQemuClient struct {
   182  	failuresRemaining int
   183  	expectedFilename  string
   184  	t                 *testing.T
   185  	returnValue       ImageInfo
   186  }
   187  
   188  func (m *mockQemuClient) GetInfo(ctx context.Context, filename string) (ImageInfo, error) {
   189  	assert.Equal(m.t, m.expectedFilename, filename)
   190  	if m.failuresRemaining > 0 {
   191  		m.failuresRemaining--
   192  		err := errors.New(inspectionError)
   193  		m.t.Logf("qemu-img returning %v", err)
   194  		return ImageInfo{}, err
   195  	}
   196  	return m.returnValue, nil
   197  }