mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	VAULT-25848 replace mholt/archiver with native go calls (#27228)
* VAULT-25848 update product code to remove mholt/archiver dependency * VAULT-25848 replace tests, still WIP while I figure out if there's a bug caught by TestDebugCommand_PartialPermissions * VAULT-25848 actually remove the dep * VAULT-25848 add headers for directories, improve test * Comment cleanup * Typo * Use %w * Typo
This commit is contained in:
		
							
								
								
									
										119
									
								
								command/debug.go
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								command/debug.go
									
									
									
									
									
								
							| @@ -4,10 +4,12 @@ | |||||||
| package command | package command | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"archive/tar" | ||||||
|  | 	"compress/gzip" | ||||||
| 	"context" | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| @@ -26,7 +28,6 @@ import ( | |||||||
| 	"github.com/hashicorp/vault/sdk/helper/jsonutil" | 	"github.com/hashicorp/vault/sdk/helper/jsonutil" | ||||||
| 	"github.com/hashicorp/vault/sdk/helper/logging" | 	"github.com/hashicorp/vault/sdk/helper/logging" | ||||||
| 	"github.com/hashicorp/vault/version" | 	"github.com/hashicorp/vault/version" | ||||||
| 	"github.com/mholt/archiver/v3" |  | ||||||
| 	"github.com/oklog/run" | 	"github.com/oklog/run" | ||||||
| 	"github.com/posener/complete" | 	"github.com/posener/complete" | ||||||
| ) | ) | ||||||
| @@ -374,7 +375,7 @@ func (c *DebugCommand) generateIndex() error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Write out file | 	// Write out file | ||||||
| 	if err := ioutil.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o600); err != nil { | 	if err := os.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o600); err != nil { | ||||||
| 		return fmt.Errorf("error generating index file; %s", err) | 		return fmt.Errorf("error generating index file; %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -778,7 +779,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { | |||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				err = ioutil.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o600) | 				err = os.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o600) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					c.captureError("pprof."+target, err) | 					c.captureError("pprof."+target, err) | ||||||
| 				} | 				} | ||||||
| @@ -796,13 +797,13 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = ioutil.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o600) | 			err = os.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o600) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.captureError("pprof.goroutines-text", err) | 				c.captureError("pprof.goroutines-text", err) | ||||||
| 			} | 			} | ||||||
| 		}() | 		}() | ||||||
|  |  | ||||||
| 		// If the our remaining duration is less than the interval value | 		// If our remaining duration is less than the interval value | ||||||
| 		// skip profile and trace. | 		// skip profile and trace. | ||||||
| 		runDuration := currentTimestamp.Sub(startTime) | 		runDuration := currentTimestamp.Sub(startTime) | ||||||
| 		if (c.flagDuration+debugDurationGrace)-runDuration < c.flagInterval { | 		if (c.flagDuration+debugDurationGrace)-runDuration < c.flagInterval { | ||||||
| @@ -820,7 +821,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = ioutil.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o600) | 			err = os.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o600) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.captureError("pprof.profile", err) | 				c.captureError("pprof.profile", err) | ||||||
| 			} | 			} | ||||||
| @@ -836,7 +837,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = ioutil.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o600) | 			err = os.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o600) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.captureError("pprof.trace", err) | 				c.captureError("pprof.trace", err) | ||||||
| 			} | 			} | ||||||
| @@ -972,7 +973,7 @@ func (c *DebugCommand) persistCollection(collection []map[string]interface{}, ou | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := ioutil.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o600); err != nil { | 	if err := os.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o600); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -984,14 +985,100 @@ func (c *DebugCommand) compress(dst string) error { | |||||||
| 		defer osutil.Umask(osutil.Umask(0o077)) | 		defer osutil.Umask(osutil.Umask(0o077)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	tgz := archiver.NewTarGz() | 	if err := archiveToTgz(c.flagOutput, dst); err != nil { | ||||||
| 	if err := tgz.Archive([]string{c.flagOutput}, dst); err != nil { | 		return fmt.Errorf("failed to compress data: %w", err) | ||||||
| 		return fmt.Errorf("failed to compress data: %s", err) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// If everything is fine up to this point, remove original directory | 	// If everything is fine up to this point, remove original directory | ||||||
| 	if err := os.RemoveAll(c.flagOutput); err != nil { | 	if err := os.RemoveAll(c.flagOutput); err != nil { | ||||||
| 		return fmt.Errorf("failed to remove data directory: %s", err) | 		return fmt.Errorf("failed to remove data directory: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // archiveToTgz compresses all the files in sourceDir to a | ||||||
|  | // a tarball at destination. | ||||||
|  | func archiveToTgz(sourceDir, destination string) error { | ||||||
|  | 	file, err := os.Create(destination) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to create file: %w", err) | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  |  | ||||||
|  | 	gzipWriter := gzip.NewWriter(file) | ||||||
|  | 	defer gzipWriter.Close() | ||||||
|  |  | ||||||
|  | 	tarWriter := tar.NewWriter(gzipWriter) | ||||||
|  | 	defer tarWriter.Close() | ||||||
|  |  | ||||||
|  | 	err = filepath.Walk(sourceDir, | ||||||
|  | 		func(filePath string, info os.FileInfo, err error) error { | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			return addFileToTar(sourceDir, filePath, tarWriter) | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // addFileToTar takes a file at filePath and adds it to the tar | ||||||
|  | // being written to by tarWriter, alongside its header. | ||||||
|  | // The tar header name will be relative. Example: If we're tarring | ||||||
|  | // a file in ~/a/b/c/foo/bar.json, the header name will be foo/bar.json | ||||||
|  | func addFileToTar(sourceDir, filePath string, tarWriter *tar.Writer) error { | ||||||
|  | 	file, err := os.Open(filePath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to open file %q: %w", filePath, err) | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  |  | ||||||
|  | 	stat, err := file.Stat() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to stat file %q: %w", filePath, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var link string | ||||||
|  | 	mode := stat.Mode() | ||||||
|  | 	if mode&os.ModeSymlink != 0 { | ||||||
|  | 		if link, err = os.Readlink(filePath); err != nil { | ||||||
|  | 			return fmt.Errorf("failed to read symlink for file %q: %w", filePath, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	tarHeader, err := tar.FileInfoHeader(stat, link) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to create tar header for file %q: %w", filePath, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// The tar header name should be relative, so remove the sourceDir from it, | ||||||
|  | 	// but preserve the last directory name. | ||||||
|  | 	// Example: If we're tarring a file in ~/a/b/c/foo/bar.json | ||||||
|  | 	// The name should be foo/bar.json | ||||||
|  | 	sourceDirExceptLastDir := filepath.Dir(sourceDir) | ||||||
|  | 	headerName := strings.TrimPrefix(filepath.Clean(filePath), filepath.Clean(sourceDirExceptLastDir)+"/") | ||||||
|  |  | ||||||
|  | 	// Directories should end with a slash. | ||||||
|  | 	if stat.IsDir() && !strings.HasSuffix(headerName, "/") { | ||||||
|  | 		headerName += "/" | ||||||
|  | 	} | ||||||
|  | 	tarHeader.Name = headerName | ||||||
|  |  | ||||||
|  | 	err = tarWriter.WriteHeader(tarHeader) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to write tar header for file %q: %w", filePath, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If it's not a regular file (e.g. link or directory) we shouldn't | ||||||
|  | 	// copy the file. The body of a tar entry (i.e. what's done by the | ||||||
|  | 	// below io.Copy call) is only required for tar files of TypeReg. | ||||||
|  | 	if tarHeader.Typeflag != tar.TypeReg { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = io.Copy(tarWriter, file) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to copy file %q into tarball: %w", filePath, err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -1008,7 +1095,7 @@ func pprofTarget(ctx context.Context, client *api.Client, target string, params | |||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	data, err := ioutil.ReadAll(resp.Body) | 	data, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -1028,7 +1115,7 @@ func pprofProfile(ctx context.Context, client *api.Client, duration time.Duratio | |||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	data, err := ioutil.ReadAll(resp.Body) | 	data, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -1048,7 +1135,7 @@ func pprofTrace(ctx context.Context, client *api.Client, duration time.Duration) | |||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	data, err := ioutil.ReadAll(resp.Body) | 	data, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,9 +5,10 @@ package command | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"archive/tar" | 	"archive/tar" | ||||||
|  | 	"compress/gzip" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| @@ -18,7 +19,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/hashicorp/cli" | 	"github.com/hashicorp/cli" | ||||||
| 	"github.com/hashicorp/vault/api" | 	"github.com/hashicorp/vault/api" | ||||||
| 	"github.com/mholt/archiver/v3" | 	"github.com/stretchr/testify/require" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func testDebugCommand(tb testing.TB) (*cli.MockUi, *DebugCommand) { | func testDebugCommand(tb testing.TB) (*cli.MockUi, *DebugCommand) { | ||||||
| @@ -35,11 +36,7 @@ func testDebugCommand(tb testing.TB) (*cli.MockUi, *DebugCommand) { | |||||||
| func TestDebugCommand_Run(t *testing.T) { | func TestDebugCommand_Run(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| 	testDir, err := ioutil.TempDir("", "vault-debug") | 	testDir := t.TempDir() | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 	cases := []struct { | 	cases := []struct { | ||||||
| 		name string | 		name string | ||||||
| @@ -104,6 +101,54 @@ func TestDebugCommand_Run(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // expectHeaderNamesInTarGzFile asserts that the expectedHeaderNames | ||||||
|  | // match exactly to the header names in the tar.gz file at tarballPath. | ||||||
|  | // Will error if there are more or less than expected. | ||||||
|  | // ignoreUnexpectedHeaders toggles ignoring the presence of headers not | ||||||
|  | // in expectedHeaderNames. | ||||||
|  | func expectHeaderNamesInTarGzFile(t *testing.T, tarballPath string, expectedHeaderNames []string, ignoreUnexpectedHeaders bool) { | ||||||
|  | 	t.Helper() | ||||||
|  |  | ||||||
|  | 	file, err := os.Open(tarballPath) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	uncompressedStream, err := gzip.NewReader(file) | ||||||
|  | 	require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	tarReader := tar.NewReader(uncompressedStream) | ||||||
|  | 	headersFoundMap := make(map[string]any) | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		header, err := tarReader.Next() | ||||||
|  | 		if err == io.EOF { | ||||||
|  | 			// We're at the end of the tar. | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		require.NoError(t, err) | ||||||
|  |  | ||||||
|  | 		// Ignore directories. | ||||||
|  | 		if header.Typeflag == tar.TypeDir { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for _, name := range expectedHeaderNames { | ||||||
|  | 			if header.Name == name { | ||||||
|  | 				headersFoundMap[header.Name] = struct{}{} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if _, ok := headersFoundMap[header.Name]; !ok && !ignoreUnexpectedHeaders { | ||||||
|  | 			t.Fatalf("unexpected file: %s", header.Name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Expect that every expectedHeader was found at some point | ||||||
|  | 	for _, name := range expectedHeaderNames { | ||||||
|  | 		if _, ok := headersFoundMap[name]; !ok { | ||||||
|  | 			t.Fatalf("missing header from tar: %s", name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestDebugCommand_Archive(t *testing.T) { | func TestDebugCommand_Archive(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| @@ -137,11 +182,7 @@ func TestDebugCommand_Archive(t *testing.T) { | |||||||
|  |  | ||||||
| 			// Create temp dirs for each test case since os.Stat and tgz.Walk | 			// Create temp dirs for each test case since os.Stat and tgz.Walk | ||||||
| 			// (called down below) exhibits raciness otherwise. | 			// (called down below) exhibits raciness otherwise. | ||||||
| 			testDir, err := ioutil.TempDir("", "vault-debug") | 			testDir := t.TempDir() | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 			defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 			client, closer := testVaultServer(t) | 			client, closer := testVaultServer(t) | ||||||
| 			defer closer() | 			defer closer() | ||||||
| @@ -177,32 +218,14 @@ func TestDebugCommand_Archive(t *testing.T) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			bundlePath := filepath.Join(testDir, basePath+expectedExt) | 			bundlePath := filepath.Join(testDir, basePath+expectedExt) | ||||||
| 			_, err = os.Stat(bundlePath) | 			_, err := os.Stat(bundlePath) | ||||||
| 			if os.IsNotExist(err) { | 			if os.IsNotExist(err) { | ||||||
| 				t.Log(ui.OutputWriter.String()) | 				t.Log(ui.OutputWriter.String()) | ||||||
| 				t.Fatal(err) | 				t.Fatal(err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			tgz := archiver.NewTarGz() | 			expectedHeaders := []string{filepath.Join(basePath, "index.json"), filepath.Join(basePath, "server_status.json")} | ||||||
| 			err = tgz.Walk(bundlePath, func(f archiver.File) error { | 			expectHeaderNamesInTarGzFile(t, bundlePath, expectedHeaders, false) | ||||||
| 				fh, ok := f.Header.(*tar.Header) |  | ||||||
| 				if !ok { |  | ||||||
| 					return fmt.Errorf("invalid file header: %#v", f.Header) |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// Ignore base directory and index file |  | ||||||
| 				if fh.Name == basePath+"/" || fh.Name == filepath.Join(basePath, "index.json") { |  | ||||||
| 					return nil |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				if fh.Name != filepath.Join(basePath, "server_status.json") { |  | ||||||
| 					return fmt.Errorf("unexpected file: %s", fh.Name) |  | ||||||
| 				} |  | ||||||
| 				return nil |  | ||||||
| 			}) |  | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -258,11 +281,7 @@ func TestDebugCommand_CaptureTargets(t *testing.T) { | |||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| 			t.Parallel() | 			t.Parallel() | ||||||
|  |  | ||||||
| 			testDir, err := ioutil.TempDir("", "vault-debug") | 			testDir := t.TempDir() | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 			defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 			client, closer := testVaultServer(t) | 			client, closer := testVaultServer(t) | ||||||
| 			defer closer() | 			defer closer() | ||||||
| @@ -287,45 +306,22 @@ func TestDebugCommand_CaptureTargets(t *testing.T) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			bundlePath := filepath.Join(testDir, basePath+debugCompressionExt) | 			bundlePath := filepath.Join(testDir, basePath+debugCompressionExt) | ||||||
| 			_, err = os.Open(bundlePath) | 			_, err := os.Open(bundlePath) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				t.Fatalf("failed to open archive: %s", err) | 				t.Fatalf("failed to open archive: %s", err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			tgz := archiver.NewTarGz() | 			expectedHeaders := []string{filepath.Join(basePath, "index.json")} | ||||||
| 			err = tgz.Walk(bundlePath, func(f archiver.File) error { | 			for _, fileName := range tc.expectedFiles { | ||||||
| 				fh, ok := f.Header.(*tar.Header) | 				expectedHeaders = append(expectedHeaders, filepath.Join(basePath, fileName)) | ||||||
| 				if !ok { |  | ||||||
| 					t.Fatalf("invalid file header: %#v", f.Header) |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// Ignore base directory and index file |  | ||||||
| 				if fh.Name == basePath+"/" || fh.Name == filepath.Join(basePath, "index.json") { |  | ||||||
| 					return nil |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				for _, fileName := range tc.expectedFiles { |  | ||||||
| 					if fh.Name == filepath.Join(basePath, fileName) { |  | ||||||
| 						return nil |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// If we reach here, it means that this is an unexpected file |  | ||||||
| 				return fmt.Errorf("unexpected file: %s", fh.Name) |  | ||||||
| 			}) |  | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} | 			} | ||||||
|  | 			expectHeaderNamesInTarGzFile(t, bundlePath, expectedHeaders, false) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestDebugCommand_Pprof(t *testing.T) { | func TestDebugCommand_Pprof(t *testing.T) { | ||||||
| 	testDir, err := ioutil.TempDir("", "vault-debug") | 	testDir := t.TempDir() | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 	client, closer := testVaultServer(t) | 	client, closer := testVaultServer(t) | ||||||
| 	defer closer() | 	defer closer() | ||||||
| @@ -379,11 +375,7 @@ func TestDebugCommand_Pprof(t *testing.T) { | |||||||
| func TestDebugCommand_IndexFile(t *testing.T) { | func TestDebugCommand_IndexFile(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| 	testDir, err := ioutil.TempDir("", "vault-debug") | 	testDir := t.TempDir() | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 	client, closer := testVaultServer(t) | 	client, closer := testVaultServer(t) | ||||||
| 	defer closer() | 	defer closer() | ||||||
| @@ -409,7 +401,7 @@ func TestDebugCommand_IndexFile(t *testing.T) { | |||||||
| 		t.Fatalf("expected %d to be %d", code, exp) | 		t.Fatalf("expected %d to be %d", code, exp) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	content, err := ioutil.ReadFile(filepath.Join(outputPath, "index.json")) | 	content, err := os.ReadFile(filepath.Join(outputPath, "index.json")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| @@ -426,11 +418,7 @@ func TestDebugCommand_IndexFile(t *testing.T) { | |||||||
| func TestDebugCommand_TimingChecks(t *testing.T) { | func TestDebugCommand_TimingChecks(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| 	testDir, err := ioutil.TempDir("", "vault-debug") | 	testDir := t.TempDir() | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 	cases := []struct { | 	cases := []struct { | ||||||
| 		name            string | 		name            string | ||||||
| @@ -585,11 +573,7 @@ func TestDebugCommand_OutputExists(t *testing.T) { | |||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| 			t.Parallel() | 			t.Parallel() | ||||||
|  |  | ||||||
| 			testDir, err := ioutil.TempDir("", "vault-debug") | 			testDir := t.TempDir() | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 			defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 			client, closer := testVaultServer(t) | 			client, closer := testVaultServer(t) | ||||||
| 			defer closer() | 			defer closer() | ||||||
| @@ -602,12 +586,12 @@ func TestDebugCommand_OutputExists(t *testing.T) { | |||||||
|  |  | ||||||
| 			// Create a conflicting file/directory | 			// Create a conflicting file/directory | ||||||
| 			if tc.compress { | 			if tc.compress { | ||||||
| 				_, err = os.Create(outputPath) | 				_, err := os.Create(outputPath) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					t.Fatal(err) | 					t.Fatal(err) | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				err = os.Mkdir(outputPath, 0o700) | 				err := os.Mkdir(outputPath, 0o700) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					t.Fatal(err) | 					t.Fatal(err) | ||||||
| 				} | 				} | ||||||
| @@ -639,11 +623,7 @@ func TestDebugCommand_OutputExists(t *testing.T) { | |||||||
| func TestDebugCommand_PartialPermissions(t *testing.T) { | func TestDebugCommand_PartialPermissions(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  |  | ||||||
| 	testDir, err := ioutil.TempDir("", "vault-debug") | 	testDir := t.TempDir() | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 	client, closer := testVaultServer(t) | 	client, closer := testVaultServer(t) | ||||||
| 	defer closer() | 	defer closer() | ||||||
| @@ -680,38 +660,14 @@ func TestDebugCommand_PartialPermissions(t *testing.T) { | |||||||
| 		t.Fatalf("failed to open archive: %s", err) | 		t.Fatalf("failed to open archive: %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	tgz := archiver.NewTarGz() | 	expectedHeaders := []string{ | ||||||
| 	err = tgz.Walk(bundlePath, func(f archiver.File) error { | 		filepath.Join(basePath, "index.json"), filepath.Join(basePath, "server_status.json"), | ||||||
| 		fh, ok := f.Header.(*tar.Header) | 		filepath.Join(basePath, "vault.log"), | ||||||
| 		if !ok { |  | ||||||
| 			t.Fatalf("invalid file header: %#v", f.Header) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Ignore base directory and index file |  | ||||||
| 		if fh.Name == basePath+"/" { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// Ignore directories, which still get created by pprof but should |  | ||||||
| 		// otherwise be empty. |  | ||||||
| 		if fh.FileInfo().IsDir() { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch { |  | ||||||
| 		case fh.Name == filepath.Join(basePath, "index.json"): |  | ||||||
| 		case fh.Name == filepath.Join(basePath, "replication_status.json"): |  | ||||||
| 		case fh.Name == filepath.Join(basePath, "server_status.json"): |  | ||||||
| 		case fh.Name == filepath.Join(basePath, "vault.log"): |  | ||||||
| 		default: |  | ||||||
| 			return fmt.Errorf("unexpected file: %s", fh.Name) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return nil |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// We set ignoreUnexpectedHeaders to true as replication_status.json is only sometimes | ||||||
|  | 	// produced. Relying on it being or not being there would be racy. | ||||||
|  | 	expectHeaderNamesInTarGzFile(t, bundlePath, expectedHeaders, true) | ||||||
| } | } | ||||||
|  |  | ||||||
| // set insecure umask to see if the files and directories get created with right permissions | // set insecure umask to see if the files and directories get created with right permissions | ||||||
| @@ -748,11 +704,7 @@ func TestDebugCommand_InsecureUmask(t *testing.T) { | |||||||
| 			// set insecure umask | 			// set insecure umask | ||||||
| 			defer syscall.Umask(syscall.Umask(0)) | 			defer syscall.Umask(syscall.Umask(0)) | ||||||
|  |  | ||||||
| 			testDir, err := ioutil.TempDir("", "vault-debug") | 			testDir := t.TempDir() | ||||||
| 			if err != nil { |  | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 			defer os.RemoveAll(testDir) |  | ||||||
|  |  | ||||||
| 			client, closer := testVaultServer(t) | 			client, closer := testVaultServer(t) | ||||||
| 			defer closer() | 			defer closer() | ||||||
| @@ -796,20 +748,22 @@ func TestDebugCommand_InsecureUmask(t *testing.T) { | |||||||
| 			// check permissions of the files within the parent directory | 			// check permissions of the files within the parent directory | ||||||
| 			switch tc.compress { | 			switch tc.compress { | ||||||
| 			case true: | 			case true: | ||||||
| 				tgz := archiver.NewTarGz() | 				file, err := os.Open(bundlePath) | ||||||
|  | 				require.NoError(t, err) | ||||||
|  |  | ||||||
| 				err = tgz.Walk(bundlePath, func(f archiver.File) error { | 				uncompressedStream, err := gzip.NewReader(file) | ||||||
| 					fh, ok := f.Header.(*tar.Header) | 				require.NoError(t, err) | ||||||
| 					if !ok { |  | ||||||
| 						return fmt.Errorf("invalid file header: %#v", f.Header) |  | ||||||
| 					} |  | ||||||
| 					err = isValidFilePermissions(fh.FileInfo()) |  | ||||||
| 					if err != nil { |  | ||||||
| 						t.Fatalf(err.Error()) |  | ||||||
| 					} |  | ||||||
| 					return nil |  | ||||||
| 				}) |  | ||||||
|  |  | ||||||
|  | 				tarReader := tar.NewReader(uncompressedStream) | ||||||
|  |  | ||||||
|  | 				for { | ||||||
|  | 					header, err := tarReader.Next() | ||||||
|  | 					if err == io.EOF { | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 					err = isValidFilePermissions(header.FileInfo()) | ||||||
|  | 					require.NoError(t, err) | ||||||
|  | 				} | ||||||
| 			case false: | 			case false: | ||||||
| 				err = filepath.Walk(bundlePath, func(path string, info os.FileInfo, err error) error { | 				err = filepath.Walk(bundlePath, func(path string, info os.FileInfo, err error) error { | ||||||
| 					err = isValidFilePermissions(info) | 					err = isValidFilePermissions(info) | ||||||
| @@ -820,9 +774,7 @@ func TestDebugCommand_InsecureUmask(t *testing.T) { | |||||||
| 				}) | 				}) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if err != nil { | 			require.NoError(t, err) | ||||||
| 				t.Fatal(err) |  | ||||||
| 			} |  | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.mod
									
									
									
									
									
								
							| @@ -172,7 +172,6 @@ require ( | |||||||
| 	github.com/kr/text v0.2.0 | 	github.com/kr/text v0.2.0 | ||||||
| 	github.com/mattn/go-colorable v0.1.13 | 	github.com/mattn/go-colorable v0.1.13 | ||||||
| 	github.com/mattn/go-isatty v0.0.20 | 	github.com/mattn/go-isatty v0.0.20 | ||||||
| 	github.com/mholt/archiver/v3 v3.5.1 |  | ||||||
| 	github.com/michaelklishin/rabbit-hole/v2 v2.12.0 | 	github.com/michaelklishin/rabbit-hole/v2 v2.12.0 | ||||||
| 	github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a | 	github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a | ||||||
| 	github.com/mitchellh/copystructure v1.2.0 | 	github.com/mitchellh/copystructure v1.2.0 | ||||||
| @@ -281,7 +280,6 @@ require ( | |||||||
| 	github.com/Microsoft/go-winio v0.6.1 // indirect | 	github.com/Microsoft/go-winio v0.6.1 // indirect | ||||||
| 	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect | 	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect | ||||||
| 	github.com/agext/levenshtein v1.2.1 // indirect | 	github.com/agext/levenshtein v1.2.1 // indirect | ||||||
| 	github.com/andybalholm/brotli v1.0.5 // indirect |  | ||||||
| 	github.com/apache/arrow/go/v15 v15.0.0 // indirect | 	github.com/apache/arrow/go/v15 v15.0.0 // indirect | ||||||
| 	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect | 	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect | ||||||
| 	github.com/aws/aws-sdk-go-v2 v1.23.4 // indirect | 	github.com/aws/aws-sdk-go-v2 v1.23.4 // indirect | ||||||
| @@ -342,7 +340,6 @@ require ( | |||||||
| 	github.com/docker/cli v25.0.5+incompatible // indirect | 	github.com/docker/cli v25.0.5+incompatible // indirect | ||||||
| 	github.com/docker/go-connections v0.4.0 // indirect | 	github.com/docker/go-connections v0.4.0 // indirect | ||||||
| 	github.com/docker/go-units v0.5.0 // indirect | 	github.com/docker/go-units v0.5.0 // indirect | ||||||
| 	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect |  | ||||||
| 	github.com/dvsekhvalnov/jose2go v1.6.0 // indirect | 	github.com/dvsekhvalnov/jose2go v1.6.0 // indirect | ||||||
| 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect | 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect | ||||||
| 	github.com/emirpasic/gods v1.18.1 // indirect | 	github.com/emirpasic/gods v1.18.1 // indirect | ||||||
| @@ -433,7 +430,6 @@ require ( | |||||||
| 	github.com/kelseyhightower/envconfig v1.4.0 // indirect | 	github.com/kelseyhightower/envconfig v1.4.0 // indirect | ||||||
| 	github.com/kevinburke/ssh_config v1.2.0 // indirect | 	github.com/kevinburke/ssh_config v1.2.0 // indirect | ||||||
| 	github.com/klauspost/cpuid/v2 v2.2.5 // indirect | 	github.com/klauspost/cpuid/v2 v2.2.5 // indirect | ||||||
| 	github.com/klauspost/pgzip v1.2.5 // indirect |  | ||||||
| 	github.com/kylelemons/godebug v1.1.0 // indirect | 	github.com/kylelemons/godebug v1.1.0 // indirect | ||||||
| 	github.com/lib/pq v1.10.9 // indirect | 	github.com/lib/pq v1.10.9 // indirect | ||||||
| 	github.com/linode/linodego v0.7.1 // indirect | 	github.com/linode/linodego v0.7.1 // indirect | ||||||
| @@ -466,7 +462,6 @@ require ( | |||||||
| 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | ||||||
| 	github.com/natefinch/atomic v1.0.1 // indirect | 	github.com/natefinch/atomic v1.0.1 // indirect | ||||||
| 	github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect | 	github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect | ||||||
| 	github.com/nwaples/rardecode v1.1.2 // indirect |  | ||||||
| 	github.com/oklog/ulid v1.3.1 // indirect | 	github.com/oklog/ulid v1.3.1 // indirect | ||||||
| 	github.com/opencontainers/go-digest v1.0.0 // indirect | 	github.com/opencontainers/go-digest v1.0.0 // indirect | ||||||
| 	github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect | 	github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect | ||||||
| @@ -504,7 +499,6 @@ require ( | |||||||
| 	github.com/tklauser/go-sysconf v0.3.10 // indirect | 	github.com/tklauser/go-sysconf v0.3.10 // indirect | ||||||
| 	github.com/tklauser/numcpus v0.4.0 // indirect | 	github.com/tklauser/numcpus v0.4.0 // indirect | ||||||
| 	github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect | 	github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect | ||||||
| 	github.com/ulikunitz/xz v0.5.11 // indirect |  | ||||||
| 	github.com/vmware/govmomi v0.18.0 // indirect | 	github.com/vmware/govmomi v0.18.0 // indirect | ||||||
| 	github.com/xanzy/ssh-agent v0.3.3 // indirect | 	github.com/xanzy/ssh-agent v0.3.3 // indirect | ||||||
| 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect | 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect | ||||||
| @@ -513,7 +507,6 @@ require ( | |||||||
| 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect | 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect | ||||||
| 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect | 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect | ||||||
| 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect | 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect | ||||||
| 	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect |  | ||||||
| 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect | 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect | ||||||
| 	github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect | 	github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect | ||||||
| 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.sum
									
									
									
									
									
								
							| @@ -774,10 +774,7 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.62.737 h1:ZJQHOp8O0RpldZ8XQwCSlpiDMkiY | |||||||
| github.com/aliyun/alibaba-cloud-sdk-go v1.62.737/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= | github.com/aliyun/alibaba-cloud-sdk-go v1.62.737/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= | ||||||
| github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 h1:nWDRPCyCltiTsANwC/n3QZH7Vww33Npq9MKqlwRzI/c= | github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 h1:nWDRPCyCltiTsANwC/n3QZH7Vww33Npq9MKqlwRzI/c= | ||||||
| github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= | github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= | ||||||
| github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= |  | ||||||
| github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||||||
| github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= |  | ||||||
| github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= |  | ||||||
| github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= | ||||||
| github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= | ||||||
| github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= | ||||||
| @@ -1009,9 +1006,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 | |||||||
| github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | ||||||
| github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= | ||||||
| github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= | ||||||
| github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= |  | ||||||
| github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= |  | ||||||
| github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= |  | ||||||
| github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= | github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= | ||||||
| github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= | github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= | ||||||
| github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= | ||||||
| @@ -1255,7 +1249,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu | |||||||
| github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | ||||||
| github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | ||||||
| github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= |  | ||||||
| github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | ||||||
| github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| @@ -1761,18 +1754,13 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL | |||||||
| github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= | ||||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||||
| github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= | github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= | ||||||
| github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= |  | ||||||
| github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= |  | ||||||
| github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | ||||||
| github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= | ||||||
| github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= | github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= | ||||||
| github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= | github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= | ||||||
| github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= |  | ||||||
| github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= | github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||||||
| github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= |  | ||||||
| github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= |  | ||||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||||
| github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||||
| github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | ||||||
| @@ -1839,8 +1827,6 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 | |||||||
| github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | ||||||
| github.com/mediocregopher/radix/v4 v4.1.4 h1:Uze6DEbEAvL+VHXUEu/EDBTkUk5CLct5h3nVSGpc6Ts= | github.com/mediocregopher/radix/v4 v4.1.4 h1:Uze6DEbEAvL+VHXUEu/EDBTkUk5CLct5h3nVSGpc6Ts= | ||||||
| github.com/mediocregopher/radix/v4 v4.1.4/go.mod h1:ajchozX/6ELmydxWeWM6xCFHVpZ4+67LXHOTOVR0nCE= | github.com/mediocregopher/radix/v4 v4.1.4/go.mod h1:ajchozX/6ELmydxWeWM6xCFHVpZ4+67LXHOTOVR0nCE= | ||||||
| github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= |  | ||||||
| github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= |  | ||||||
| github.com/michaelklishin/rabbit-hole/v2 v2.12.0 h1:946p6jOYFcVJdtBBX8MwXvuBkpPjwm1Nm2Qg8oX+uFk= | github.com/michaelklishin/rabbit-hole/v2 v2.12.0 h1:946p6jOYFcVJdtBBX8MwXvuBkpPjwm1Nm2Qg8oX+uFk= | ||||||
| github.com/michaelklishin/rabbit-hole/v2 v2.12.0/go.mod h1:AN/3zyz7d++OHf+4WUo/LR0+Q5nlPHMaXasIsG/mPY0= | github.com/michaelklishin/rabbit-hole/v2 v2.12.0/go.mod h1:AN/3zyz7d++OHf+4WUo/LR0+Q5nlPHMaXasIsG/mPY0= | ||||||
| github.com/microsoft/go-mssqldb v1.5.0 h1:CgENxkwtOBNj3Jg6T1X209y2blCfTTcwuOlznd2k9fk= | github.com/microsoft/go-mssqldb v1.5.0 h1:CgENxkwtOBNj3Jg6T1X209y2blCfTTcwuOlznd2k9fk= | ||||||
| @@ -1935,9 +1921,6 @@ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ | |||||||
| github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= | github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= | ||||||
| github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= | github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= | ||||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||||
| github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= |  | ||||||
| github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= |  | ||||||
| github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= |  | ||||||
| github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | ||||||
| github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= | ||||||
| github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= | ||||||
| @@ -2002,7 +1985,6 @@ github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk | |||||||
| github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= | github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= | ||||||
| github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= | github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= | ||||||
| github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= | github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= | ||||||
| github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= |  | ||||||
| github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||||||
| github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= | github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= | ||||||
| github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||||||
| @@ -2196,10 +2178,6 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK | |||||||
| github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= | github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= | ||||||
| github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | ||||||
| github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | ||||||
| github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= |  | ||||||
| github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= |  | ||||||
| github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= |  | ||||||
| github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= |  | ||||||
| github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= | github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= | ||||||
| github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= | github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= | ||||||
| github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= | ||||||
| @@ -2219,8 +2197,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo | |||||||
| github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= | ||||||
| github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= | ||||||
| github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= | ||||||
| github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= |  | ||||||
| github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= |  | ||||||
| github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= | ||||||
| github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= | ||||||
| github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Violet Hynes
					Violet Hynes