Skip to content

Commit

Permalink
Implement Kopia storage helpers (#2655)
Browse files Browse the repository at this point in the history
* Add safecli dependency

* add new flag implementations based on the safecli package for the Kopia CLI

* apply go fmt

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add common Kopia args and flags

* Add Kopia storage core flags

* Fix Apply and test.Suit

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Remove variadic args for Common and Cache flags

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* pkg/kopia/cli/internal/flag is implemented in the safecli@v0.0.4 now

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add pkg/kopia/cli package

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* go mod tidy

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add Kopia storage helpers

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Convert common flags from vars to funcs

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add safecli dependency

* add new flag implementations based on the safecli package for the Kopia CLI

* apply go fmt

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Fix Apply and test.Suit

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* pkg/kopia/cli/internal/flag is implemented in the safecli@v0.0.4 now

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add pkg/kopia/cli package

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* go mod tidy

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Update safecli to v0.0.5

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Update safecli to v0.0.6

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Fix tests

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add Location.IsPointInTypeSupported

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Add tests for Location.IsPointInTypeSupported

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* Fix formatting

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* organize imports

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

* organize imports

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>

---------

Signed-off-by: pavel.larkin <pavel.larkin@veeam.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
plar and mergify[bot] committed Mar 5, 2024
1 parent ba26708 commit 30e4f5c
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pkg/kopia/cli/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ var (
// ErrInvalidID is returned when the ID is empty.
ErrInvalidID = errors.New("invalid ID")
)

// storage errors
var (
// ErrUnsupportedStorage is returned when the storage is not supported.
ErrUnsupportedStorage = errors.New("unsupported storage")
)
72 changes: 72 additions & 0 deletions pkg/kopia/cli/internal/location.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal

import (
"strconv"
"strings"

rs "github.com/kanisterio/kanister/pkg/secrets/repositoryserver"
)

// Location is a map of key-value pairs that represent different storage properties.
type Location map[string][]byte

// Type returns the location type.
func (l Location) Type() rs.LocType {
return rs.LocType(string(l[rs.TypeKey]))
}

// Region returns the location region.
func (l Location) Region() string {
return string(l[rs.RegionKey])
}

// BucketName returns the location bucket name.
func (l Location) BucketName() string {
return string(l[rs.BucketKey])
}

// Endpoint returns the location endpoint.
func (l Location) Endpoint() string {
return string(l[rs.EndpointKey])
}

// Prefix returns the location prefix.
func (l Location) Prefix() string {
return string(l[rs.PrefixKey])
}

// IsInsecureEndpoint returns true if the location endpoint is insecure/http.
func (l Location) IsInsecureEndpoint() bool {
return strings.HasPrefix(l.Endpoint(), "http:")
}

// HasSkipSSLVerify returns true if the location has skip SSL verification.
func (l Location) HasSkipSSLVerify() bool {
v, _ := strconv.ParseBool(string(l[rs.SkipSSLVerifyKey]))
return v
}

// IsPointInTimeSupported returns true if the location supports point-in-time recovery.
// Currently, only S3 and Azure support point-in-time recovery.
func (l Location) IsPointInTypeSupported() bool {
switch l.Type() {
case rs.LocTypeAzure, rs.LocTypeS3, rs.LocTypes3Compliant:
return true
default:
return false
}
}
135 changes: 135 additions & 0 deletions pkg/kopia/cli/internal/location_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2024 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal_test

import (
"testing"

"gopkg.in/check.v1"

"github.com/kanisterio/kanister/pkg/kopia/cli/internal"
rs "github.com/kanisterio/kanister/pkg/secrets/repositoryserver"
)

func TestLocation(t *testing.T) { check.TestingT(t) }

type LocationSuite struct{}

var _ = check.Suite(&LocationSuite{})

func (s *LocationSuite) TestLocation(c *check.C) {
type expected struct {
Type rs.LocType
Region string
BucketName string
Endpoint string
Prefix string
IsInsecure bool
HasSkipSSLVerify bool
IsPITSupported bool
}

tests := []struct {
name string
location internal.Location
expected expected
}{
{
name: "Test with no fields",
location: internal.Location{},
expected: expected{},
},
{
name: "Test with all fields",
location: internal.Location{
rs.TypeKey: []byte("Type1"),
rs.RegionKey: []byte("Region1"),
rs.BucketKey: []byte("Bucket1"),
rs.EndpointKey: []byte("http://Endpoint1"),
rs.PrefixKey: []byte("Prefix1"),
rs.SkipSSLVerifyKey: []byte("true"),
},
expected: expected{
Type: "Type1",
Region: "Region1",
BucketName: "Bucket1",
Endpoint: "http://Endpoint1",
Prefix: "Prefix1",
IsInsecure: true,
HasSkipSSLVerify: true,
},
},
{
name: "Test PIT Support for S3 Compliant",
location: internal.Location{
rs.TypeKey: []byte(rs.LocTypes3Compliant),
},
expected: expected{
Type: "s3Compliant",
IsPITSupported: true,
},
},
{
name: "Test PIT Support for S3",
location: internal.Location{
rs.TypeKey: []byte(rs.LocTypeS3),
},
expected: expected{
Type: "s3",
IsPITSupported: true,
},
},
{
name: "Test PIT Support for Azure",
location: internal.Location{
rs.TypeKey: []byte(rs.LocTypeAzure),
},
expected: expected{
Type: "azure",
IsPITSupported: true,
},
},
{
name: "Test No PIT Support for GCS",
location: internal.Location{
rs.TypeKey: []byte(rs.LocTypeGCS),
},
expected: expected{
Type: "gcs",
IsPITSupported: false,
},
},
{
name: "Test No PIT Support for FS",
location: internal.Location{
rs.TypeKey: []byte(rs.LocTypeFilestore),
},
expected: expected{
Type: "filestore",
IsPITSupported: false,
},
},
}
for _, test := range tests {
c.Check(test.location.Type(), check.Equals, test.expected.Type)
c.Check(test.location.Region(), check.Equals, test.expected.Region)
c.Check(test.location.BucketName(), check.Equals, test.expected.BucketName)
c.Check(test.location.Endpoint(), check.Equals, test.expected.Endpoint)
c.Check(test.location.Prefix(), check.Equals, test.expected.Prefix)
c.Check(test.location.IsInsecureEndpoint(), check.Equals, test.expected.IsInsecure)
c.Check(test.location.HasSkipSSLVerify(), check.Equals, test.expected.HasSkipSSLVerify)
c.Check(test.location.IsPointInTypeSupported(), check.Equals, test.expected.IsPITSupported)
}
}
45 changes: 45 additions & 0 deletions pkg/kopia/cli/internal/log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
"context"
"io"

"github.com/kanisterio/kanister/pkg/field"
"github.com/kanisterio/kanister/pkg/log"
)

// NopLogger is a logger that does nothing.
// TODO: Move to log package
type NopLogger struct{}

// Print does nothing.
func (NopLogger) Print(msg string, fields ...field.M) {
}

// PrintTo does nothing.
func (NopLogger) PrintTo(w io.Writer, msg string, fields ...field.M) {
}

// WithContext does nothing.
func (NopLogger) WithContext(ctx context.Context) log.Logger {
return &NopLogger{}
}

// WithError does nothing.
func (NopLogger) WithError(err error) log.Logger {
return &NopLogger{}
}
28 changes: 28 additions & 0 deletions pkg/kopia/cli/internal/path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2022 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal

import (
"path"
)

// GenerateFullRepoPath generates the full repository path.
// If the location-specific prefix is empty, the repository-specific prefix is returned.
func GenerateFullRepoPath(locPrefix, repoPathPrefix string) string {
if locPrefix != "" {
return path.Join(locPrefix, repoPathPrefix) + "/"
}
return repoPathPrefix
}
52 changes: 52 additions & 0 deletions pkg/kopia/cli/internal/path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2024 The Kanister Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal_test

import (
"testing"

"gopkg.in/check.v1"

"github.com/kanisterio/kanister/pkg/kopia/cli/internal"
)

func TestPath(t *testing.T) { check.TestingT(t) }

type PathSuite struct{}

var _ = check.Suite(&PathSuite{})

func (s *PathSuite) TestGenerateFullRepoPath(c *check.C) {
tests := []struct {
locPrefix string
repoPathPrefix string
expected string
}{
{
locPrefix: "",
repoPathPrefix: "repo",
expected: "repo",
},
{
locPrefix: "loc",
repoPathPrefix: "repo",
expected: "loc/repo/",
},
}
for _, test := range tests {
got := internal.GenerateFullRepoPath(test.locPrefix, test.repoPathPrefix)
c.Check(got, check.Equals, test.expected)
}
}

0 comments on commit 30e4f5c

Please sign in to comment.