diff --git a/README.md b/README.md index f03d1cdf..f2a21dff 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,11 @@ metadata: Applying this yaml file will create a Kubernetes Secret with the name `` and contents from the location specified at the specified Item Path. +The contents of the Kubernetes secret will be key-value pairs in which the keys are the fields of the 1Password item and the values are the corresponding values stored in 1Password. +In case of fields that store files, the file's contents will be used as the value. + +Within an item, if both a field storing a file and a field of another type have the same name, the file field will be ignored and the other field will take precedence. + Note: Deleting the Deployment that you've created will automatically delete the created Kubernetes Secret only if the deployment is still annotated with `operator.1password.io/item-path` and `operator.1password.io/item-name` and no other deployment is using the secret. If a 1Password Item that is linked to a Kubernetes Secret is updated within the POLLING_INTERVAL the associated Kubernetes Secret will be updated. However, if you do not want a specific secret to be updated you can add the tag `operator.1password.io:ignore-secret` to the item stored in 1Password. While this tag is in place, any updates made to an item will not trigger an update to the associated secret in Kubernetes. diff --git a/go.mod b/go.mod index 4327e119..920c69ac 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/1Password/onepassword-operator go 1.13 require ( - github.com/1Password/connect-sdk-go v1.0.1 + github.com/1Password/connect-sdk-go v1.2.0 github.com/operator-framework/operator-sdk v0.19.0 github.com/prometheus/common v0.14.0 // indirect github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 k8s.io/api v0.18.2 k8s.io/apimachinery v0.18.2 k8s.io/client-go v12.0.0+incompatible diff --git a/go.sum b/go.sum index 470bc3fa..bf98094f 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeS dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/1Password/connect-sdk-go v1.0.1 h1:BOeMIxVk6/ISmLNWUkSxEbVI7tNr5+aNXIobMM0/I0U= github.com/1Password/connect-sdk-go v1.0.1/go.mod h1:br2BWk2sqgJFnOFK5WSDfBBmwQ6E7hV9LoPqrtHGRNY= +github.com/1Password/connect-sdk-go v1.2.0 h1:WbIvmbDUpA89nyH0l3LF2iRSFJAv86d2D7IjVNjw6iw= +github.com/1Password/connect-sdk-go v1.2.0/go.mod h1:qK2bF/GweAq812xj+HGfbauaE6cKX1MXfKhpAvoHEq8= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -885,6 +887,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thanos-io/thanos v0.11.0/go.mod h1:N/Yes7J68KqvmY+xM6J5CJqEvWIvKSR5sqGtmuD6wDc= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder.go b/pkg/kubernetessecrets/kubernetes_secrets_builder.go index d3fa890d..34157376 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder.go @@ -12,6 +12,7 @@ import ( errs "errors" "github.com/1Password/connect-sdk-go/onepassword" + "github.com/1Password/onepassword-operator/pkg/utils" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -94,12 +95,12 @@ func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotation Annotations: annotations, Labels: labels, }, - Data: BuildKubernetesSecretData(item.Fields), + Data: BuildKubernetesSecretData(item.Fields, item.Files), Type: corev1.SecretType(secretType), } } -func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byte { +func BuildKubernetesSecretData(fields []*onepassword.ItemField, files []*onepassword.File) map[string][]byte { secretData := map[string][]byte{} for i := 0; i < len(fields); i++ { if fields[i].Value != "" { @@ -107,6 +108,23 @@ func BuildKubernetesSecretData(fields []*onepassword.ItemField) map[string][]byt secretData[key] = []byte(fields[i].Value) } } + + // populate unpopulated fields from files + for _, file := range files { + content, err := file.Content() + if err != nil { + log.Error(err, "Could not load contents of file %s", file.Name) + continue + } + if content != nil { + key := file.Name + if secretData[key] == nil { + secretData[key] = content + } else { + log.Info(fmt.Sprintf("File '%s' ignored because of a field with the same name", file.Name)) + } + } + } return secretData } diff --git a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go index e0fd7323..2bc9faf0 100644 --- a/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go +++ b/pkg/kubernetessecrets/kubernetes_secrets_builder_test.go @@ -98,7 +98,7 @@ func TestUpdateKubernetesSecretFromOnePasswordItem(t *testing.T) { func TestBuildKubernetesSecretData(t *testing.T) { fields := generateFields(5) - secretData := BuildKubernetesSecretData(fields) + secretData := BuildKubernetesSecretData(fields, nil) if len(secretData) != len(fields) { t.Errorf("Unexpected number of secret fields returned. Expected 3, got %v", len(secretData)) } diff --git a/pkg/mocks/mocksecretserver.go b/pkg/mocks/mocksecretserver.go index 489182f1..29c76649 100644 --- a/pkg/mocks/mocksecretserver.go +++ b/pkg/mocks/mocksecretserver.go @@ -7,6 +7,7 @@ import ( type TestClient struct { GetVaultsFunc func() ([]onepassword.Vault, error) GetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error) + GetVaultFunc func(uuid string) (*onepassword.Vault, error) GetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error) GetItemsFunc func(vaultUUID string) ([]onepassword.Item, error) GetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error) @@ -14,11 +15,14 @@ type TestClient struct { CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error + GetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) + GetFileContentFunc func(file *onepassword.File) ([]byte, error) } var ( GetGetVaultsFunc func() ([]onepassword.Vault, error) DoGetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error) + DoGetVaultFunc func(uuid string) (*onepassword.Vault, error) GetGetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error) DoGetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error) DoGetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error) @@ -26,6 +30,8 @@ var ( DoDeleteItemFunc func(item *onepassword.Item, vaultUUID string) error DoGetItemsFunc func(vaultUUID string) ([]onepassword.Item, error) DoUpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) + DoGetFileFunc func(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) + DoGetFileContentFunc func(file *onepassword.File) ([]byte, error) ) // Do is the mock client's `Do` func @@ -37,6 +43,10 @@ func (m *TestClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error) return DoGetVaultsByTitleFunc(title) } +func (m *TestClient) GetVault(uuid string) (*onepassword.Vault, error) { + return DoGetVaultFunc(uuid) +} + func (m *TestClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) { return GetGetItemFunc(uuid, vaultUUID) } @@ -64,3 +74,11 @@ func (m *TestClient) DeleteItem(item *onepassword.Item, vaultUUID string) error func (m *TestClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { return DoUpdateItemFunc(item, vaultUUID) } + +func (m *TestClient) GetFile(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) { + return DoGetFileFunc(uuid, itemUUID, vaultUUID) +} + +func (m *TestClient) GetFileContent(file *onepassword.File) ([]byte, error) { + return DoGetFileContentFunc(file) +} diff --git a/pkg/onepassword/items.go b/pkg/onepassword/items.go index 11c4914b..c022f5d5 100644 --- a/pkg/onepassword/items.go +++ b/pkg/onepassword/items.go @@ -30,6 +30,14 @@ func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*one if err != nil { return nil, err } + + for _, file := range item.Files { + _, err := opConnectClient.GetFileContent(file) + if err != nil { + return nil, err + } + } + return item, nil } diff --git a/vendor/github.com/1Password/connect-sdk-go/LICENSE b/vendor/github.com/1Password/connect-sdk-go/LICENSE new file mode 100644 index 00000000..340215c6 --- /dev/null +++ b/vendor/github.com/1Password/connect-sdk-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 1Password + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/1Password/connect-sdk-go/connect/client.go b/vendor/github.com/1Password/connect-sdk-go/connect/client.go index f26a402a..16b1832e 100644 --- a/vendor/github.com/1Password/connect-sdk-go/connect/client.go +++ b/vendor/github.com/1Password/connect-sdk-go/connect/client.go @@ -3,6 +3,7 @@ package connect import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -10,11 +11,12 @@ import ( "net/url" "os" - "github.com/1Password/connect-sdk-go/onepassword" opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" jaegerClientConfig "github.com/uber/jaeger-client-go/config" "github.com/uber/jaeger-client-go/zipkin" + + "github.com/1Password/connect-sdk-go/onepassword" ) const ( @@ -24,6 +26,7 @@ const ( // Client Represents an available 1Password Connect API to connect to type Client interface { GetVaults() ([]onepassword.Vault, error) + GetVault(uuid string) (*onepassword.Vault, error) GetVaultsByTitle(uuid string) ([]onepassword.Vault, error) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) GetItems(vaultUUID string) ([]onepassword.Item, error) @@ -32,6 +35,8 @@ type Client interface { CreateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) DeleteItem(item *onepassword.Item, vaultUUID string) error + GetFile(fileUUID string, itemUUID string, vaultUUID string) (*onepassword.File, error) + GetFileContent(file *onepassword.File) ([]byte, error) } type httpClient interface { @@ -112,21 +117,39 @@ func (rs *restClient) GetVaults() ([]onepassword.Vault, error) { return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to retrieve vaults. Receieved %q for %q", response.Status, vaultURL) + var vaults []onepassword.Vault + if err := parseResponse(response, http.StatusOK, &vaults); err != nil { + return nil, err + } + + return vaults, nil +} + +// GetVaults Get a list of all available vaults +func (rs *restClient) GetVault(uuid string) (*onepassword.Vault, error) { + if uuid == "" { + return nil, errors.New("no uuid provided") } - body, err := ioutil.ReadAll(response.Body) + span := rs.tracer.StartSpan("GetVault") + defer span.Finish() + + vaultURL := fmt.Sprintf("/v1/vaults/%s", uuid) + request, err := rs.buildRequest(http.MethodGet, vaultURL, http.NoBody, span) if err != nil { return nil, err } - vaults := []onepassword.Vault{} - if err := json.Unmarshal(body, &vaults); err != nil { + response, err := rs.client.Do(request) + if err != nil { + return nil, err + } + var vault onepassword.Vault + if err := parseResponse(response, http.StatusOK, &vault); err != nil { return nil, err } - return vaults, nil + return &vault, nil } func (rs *restClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error) { @@ -145,17 +168,8 @@ func (rs *restClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to retrieve vaults. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - vaults := []onepassword.Vault{} - if err := json.Unmarshal(body, &vaults); err != nil { + var vaults []onepassword.Vault + if err := parseResponse(response, http.StatusOK, &vaults); err != nil { return nil, err } @@ -177,18 +191,8 @@ func (rs *restClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, if err != nil { return nil, err } - - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - item := onepassword.Item{} - if err := json.Unmarshal(body, &item); err != nil { + var item onepassword.Item + if err := parseResponse(response, http.StatusOK, &item); err != nil { return nil, err } @@ -226,17 +230,8 @@ func (rs *restClient) GetItemsByTitle(title string, vaultUUID string) ([]onepass return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - items := []onepassword.Item{} - if err := json.Unmarshal(body, &items); err != nil { + var items []onepassword.Item + if err := parseResponse(response, http.StatusOK, &items); err != nil { return nil, err } @@ -258,17 +253,8 @@ func (rs *restClient) GetItems(vaultUUID string) ([]onepassword.Item, error) { return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to retrieve items. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - items := []onepassword.Item{} - if err := json.Unmarshal(body, &items); err != nil { + var items []onepassword.Item + if err := parseResponse(response, http.StatusOK, &items); err != nil { return nil, err } @@ -296,17 +282,8 @@ func (rs *restClient) CreateItem(item *onepassword.Item, vaultUUID string) (*one return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to create item. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - newItem := onepassword.Item{} - if err := json.Unmarshal(body, &newItem); err != nil { + var newItem onepassword.Item + if err := parseResponse(response, http.StatusOK, &newItem); err != nil { return nil, err } @@ -334,17 +311,8 @@ func (rs *restClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*one return nil, err } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unable to update item. Receieved %q for %q", response.Status, itemURL) - } - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - newItem := onepassword.Item{} - if err := json.Unmarshal(body, &newItem); err != nil { + var newItem onepassword.Item + if err := parseResponse(response, http.StatusOK, &newItem); err != nil { return nil, err } @@ -367,13 +335,73 @@ func (rs *restClient) DeleteItem(item *onepassword.Item, vaultUUID string) error return err } - if response.StatusCode != http.StatusNoContent { - return fmt.Errorf("Unable to retrieve item. Receieved %q for %q", response.Status, itemURL) + if err := parseResponse(response, http.StatusNoContent, nil); err != nil { + return err } return nil } +// GetFile Get a specific File in a specified item. +// This does not include the file contents. Call GetFileContent() to load the file's content. +func (rs *restClient) GetFile(uuid string, itemUUID string, vaultUUID string) (*onepassword.File, error) { + span := rs.tracer.StartSpan("GetFile") + defer span.Finish() + + itemURL := fmt.Sprintf("/v1/vaults/%s/items/%s/files/%s", vaultUUID, itemUUID, uuid) + request, err := rs.buildRequest(http.MethodGet, itemURL, http.NoBody, span) + if err != nil { + return nil, err + } + + response, err := rs.client.Do(request) + if err != nil { + return nil, err + } + if err := expectMinimumConnectVersion(response, version{1, 3, 0}); err != nil { + return nil, err + } + + var file onepassword.File + if err := parseResponse(response, http.StatusOK, &file); err != nil { + return nil, err + } + + return &file, nil +} + +// GetFileContent retrieves the file's content. +// If the file's content have previously been fetched, those contents are returned without making another request. +func (rs *restClient) GetFileContent(file *onepassword.File) ([]byte, error) { + if content, err := file.Content(); err == nil { + return content, nil + } + + span := rs.tracer.StartSpan("GetFileContent") + defer span.Finish() + + request, err := rs.buildRequest(http.MethodGet, file.ContentPath, http.NoBody, span) + if err != nil { + return nil, err + } + + response, err := rs.client.Do(request) + if err != nil { + return nil, err + } + if err := expectMinimumConnectVersion(response, version{1, 3, 0}); err != nil { + return nil, err + } + + content, err := readResponseBody(response, http.StatusOK) + if err != nil { + return nil, err + } + + file.SetContent(content) + return content, nil +} + func (rs *restClient) buildRequest(method string, path string, body io.Reader, span opentracing.Span) (*http.Request, error) { url := fmt.Sprintf("%s%s", rs.URL, path) @@ -394,3 +422,32 @@ func (rs *restClient) buildRequest(method string, path string, body io.Reader, s return request, nil } + +func parseResponse(resp *http.Response, expectedStatusCode int, result interface{}) error { + body, err := readResponseBody(resp, expectedStatusCode) + if err != nil { + return err + } + if result != nil { + if err := json.Unmarshal(body, result); err != nil { + return fmt.Errorf("decoding response: %s", err) + } + } + return nil +} + +func readResponseBody(resp *http.Response, expectedStatusCode int) ([]byte, error) { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != expectedStatusCode { + var errResp *onepassword.Error + if err := json.Unmarshal(body, &errResp); err != nil { + return nil, fmt.Errorf("decoding error response: %s", err) + } + return nil, errResp + } + return body, nil +} diff --git a/vendor/github.com/1Password/connect-sdk-go/connect/version.go b/vendor/github.com/1Password/connect-sdk-go/connect/version.go index 61f91956..b654f8bb 100644 --- a/vendor/github.com/1Password/connect-sdk-go/connect/version.go +++ b/vendor/github.com/1Password/connect-sdk-go/connect/version.go @@ -1,5 +1,104 @@ package connect +import ( + "errors" + "fmt" + "net/http" + "strconv" + "strings" +) + // SDKVersion is the latest Semantic Version of the library // Do not rename this variable without changing the regex in the Makefile -const SDKVersion = "1.0.1" +const SDKVersion = "1.2.0" + +const VersionHeaderKey = "1Password-Connect-Version" + +// expectMinimumConnectVersion returns an error if the provided minimum version for Connect is lower than the version +// reported in the response from Connect. +func expectMinimumConnectVersion(resp *http.Response, minimumVersion version) error { + serverVersion, err := getServerVersion(resp) + if err != nil { + // Return gracefully if server version cannot be determined reliably + return nil + } + if !serverVersion.IsGreaterOrEqualThan(minimumVersion) { + return fmt.Errorf("need at least version %s of Connect for this function, detected version %s. Please update your Connect server", minimumVersion, serverVersion) + } + return nil +} + +func getServerVersion(resp *http.Response) (serverVersion, error) { + versionHeader := resp.Header.Get(VersionHeaderKey) + if versionHeader == "" { + // The last version without the version header was v1.2.0 + return serverVersion{ + version: version{1, 2, 0}, + orEarlier: true, + }, nil + } + return parseServerVersion(versionHeader) +} + +type version struct { + major int + minor int + patch int +} + +// serverVersion describes the version reported by the server. +type serverVersion struct { + version + // orEarlier is true if the version is derived from the lack of a version header from the server. + orEarlier bool +} + +func (v version) String() string { + return fmt.Sprintf("%d.%d.%d", v.major, v.minor, v.patch) +} + +func (v serverVersion) String() string { + if v.orEarlier { + return v.version.String() + " (or earlier)" + } + return v.version.String() +} + +// IsGreaterOrEqualThan returns true if the lefthand-side version is equal to or or a higher version than the provided +// minimum according to the semantic versioning rules. +func (v version) IsGreaterOrEqualThan(min version) bool { + if v.major != min.major { + // Different major version + return v.major > min.major + } + + if v.minor != min.minor { + // Same major, but different minor version + return v.minor > min.minor + } + + // Same major and minor version + return v.patch >= min.patch +} + +func parseServerVersion(v string) (serverVersion, error) { + spl := strings.Split(v, ".") + if len(spl) != 3 { + return serverVersion{}, errors.New("wrong length") + } + var res [3]int + for i := range res { + tmp, err := strconv.Atoi(spl[i]) + if err != nil { + return serverVersion{}, err + } + res[i] = tmp + } + return serverVersion{ + version: version{ + major: res[0], + minor: res[1], + patch: res[2], + }, + }, nil +} diff --git a/vendor/github.com/1Password/connect-sdk-go/onepassword/errors.go b/vendor/github.com/1Password/connect-sdk-go/onepassword/errors.go new file mode 100644 index 00000000..329a0b33 --- /dev/null +++ b/vendor/github.com/1Password/connect-sdk-go/onepassword/errors.go @@ -0,0 +1,21 @@ +package onepassword + +import "fmt" + +// Error is an error returned by the Connect API. +type Error struct { + StatusCode int `json:"status"` + Message string `json:"message"` +} + +func (e *Error) Error() string { + return fmt.Sprintf("status %d: %s", e.StatusCode, e.Message) +} + +func (e *Error) Is(target error) bool { + t, ok := target.(*Error) + if !ok { + return false + } + return t.Message == e.Message && t.StatusCode == e.StatusCode +} diff --git a/vendor/github.com/1Password/connect-sdk-go/onepassword/files.go b/vendor/github.com/1Password/connect-sdk-go/onepassword/files.go new file mode 100644 index 00000000..a34692fc --- /dev/null +++ b/vendor/github.com/1Password/connect-sdk-go/onepassword/files.go @@ -0,0 +1,49 @@ +package onepassword + +import ( + "encoding/json" + "errors" +) + +type File struct { + ID string `json:"id"` + Name string `json:"name"` + Section *ItemSection `json:"section,omitempty"` + Size int `json:"size"` + ContentPath string `json:"content_path"` + content []byte +} + +func (f *File) UnmarshalJSON(data []byte) error { + var jsonFile struct { + ID string `json:"id"` + Name string `json:"name"` + Section *ItemSection `json:"section,omitempty"` + Size int `json:"size"` + ContentPath string `json:"content_path"` + Content []byte `json:"content,omitempty"` + } + if err := json.Unmarshal(data, &jsonFile); err != nil { + return err + } + f.ID = jsonFile.ID + f.Name = jsonFile.Name + f.Section = jsonFile.Section + f.Size = jsonFile.Size + f.ContentPath = jsonFile.ContentPath + f.content = jsonFile.Content + return nil +} + +// Content returns the content of the file if they have been loaded and returns an error if they have not been loaded. +// Use `client.GetFileContent(file *File)` instead to make sure the content is fetched automatically if not present. +func (f *File) Content() ([]byte, error) { + if f.content == nil { + return nil, errors.New("file content not loaded") + } + return f.content, nil +} + +func (f *File) SetContent(content []byte) { + f.content = content +} diff --git a/vendor/github.com/1Password/connect-sdk-go/onepassword/items.go b/vendor/github.com/1Password/connect-sdk-go/onepassword/items.go index 71bc4097..783ca8ea 100644 --- a/vendor/github.com/1Password/connect-sdk-go/onepassword/items.go +++ b/vendor/github.com/1Password/connect-sdk-go/onepassword/items.go @@ -28,6 +28,7 @@ const ( Document ItemCategory = "DOCUMENT" EmailAccount ItemCategory = "EMAIL_ACCOUNT" SocialSecurityNumber ItemCategory = "SOCIAL_SECURITY_NUMBER" + ApiCredential ItemCategory = "API_CREDENTIAL" Custom ItemCategory = "CUSTOM" ) @@ -39,7 +40,7 @@ func (ic *ItemCategory) UnmarshalJSON(b []byte) error { switch category { case Login, Password, Server, Database, CreditCard, Membership, Passport, SoftwareLicense, OutdoorLicense, SecureNote, WirelessRouter, BankAccount, DriverLicense, Identity, RewardProgram, - Document, EmailAccount, SocialSecurityNumber: + Document, EmailAccount, SocialSecurityNumber, ApiCredential: *ic = category default: *ic = Custom @@ -64,6 +65,7 @@ type Item struct { Sections []*ItemSection `json:"sections,omitempty"` Fields []*ItemField `json:"fields,omitempty"` + Files []*File `json:"files,omitempty"` LastEditedBy string `json:"lastEditedBy,omitempty"` CreatedAt time.Time `json:"createdAt,omitempty"` diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index dc200395..41649d26 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -13,12 +13,42 @@ const ( compareGreater ) +var ( + intType = reflect.TypeOf(int(1)) + int8Type = reflect.TypeOf(int8(1)) + int16Type = reflect.TypeOf(int16(1)) + int32Type = reflect.TypeOf(int32(1)) + int64Type = reflect.TypeOf(int64(1)) + + uintType = reflect.TypeOf(uint(1)) + uint8Type = reflect.TypeOf(uint8(1)) + uint16Type = reflect.TypeOf(uint16(1)) + uint32Type = reflect.TypeOf(uint32(1)) + uint64Type = reflect.TypeOf(uint64(1)) + + float32Type = reflect.TypeOf(float32(1)) + float64Type = reflect.TypeOf(float64(1)) + + stringType = reflect.TypeOf("") +) + func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { + obj1Value := reflect.ValueOf(obj1) + obj2Value := reflect.ValueOf(obj2) + + // throughout this switch we try and avoid calling .Convert() if possible, + // as this has a pretty big performance impact switch kind { case reflect.Int: { - intobj1 := obj1.(int) - intobj2 := obj2.(int) + intobj1, ok := obj1.(int) + if !ok { + intobj1 = obj1Value.Convert(intType).Interface().(int) + } + intobj2, ok := obj2.(int) + if !ok { + intobj2 = obj2Value.Convert(intType).Interface().(int) + } if intobj1 > intobj2 { return compareGreater, true } @@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int8: { - int8obj1 := obj1.(int8) - int8obj2 := obj2.(int8) + int8obj1, ok := obj1.(int8) + if !ok { + int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) + } + int8obj2, ok := obj2.(int8) + if !ok { + int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) + } if int8obj1 > int8obj2 { return compareGreater, true } @@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int16: { - int16obj1 := obj1.(int16) - int16obj2 := obj2.(int16) + int16obj1, ok := obj1.(int16) + if !ok { + int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) + } + int16obj2, ok := obj2.(int16) + if !ok { + int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) + } if int16obj1 > int16obj2 { return compareGreater, true } @@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int32: { - int32obj1 := obj1.(int32) - int32obj2 := obj2.(int32) + int32obj1, ok := obj1.(int32) + if !ok { + int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) + } + int32obj2, ok := obj2.(int32) + if !ok { + int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) + } if int32obj1 > int32obj2 { return compareGreater, true } @@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Int64: { - int64obj1 := obj1.(int64) - int64obj2 := obj2.(int64) + int64obj1, ok := obj1.(int64) + if !ok { + int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) + } + int64obj2, ok := obj2.(int64) + if !ok { + int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) + } if int64obj1 > int64obj2 { return compareGreater, true } @@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint: { - uintobj1 := obj1.(uint) - uintobj2 := obj2.(uint) + uintobj1, ok := obj1.(uint) + if !ok { + uintobj1 = obj1Value.Convert(uintType).Interface().(uint) + } + uintobj2, ok := obj2.(uint) + if !ok { + uintobj2 = obj2Value.Convert(uintType).Interface().(uint) + } if uintobj1 > uintobj2 { return compareGreater, true } @@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint8: { - uint8obj1 := obj1.(uint8) - uint8obj2 := obj2.(uint8) + uint8obj1, ok := obj1.(uint8) + if !ok { + uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) + } + uint8obj2, ok := obj2.(uint8) + if !ok { + uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) + } if uint8obj1 > uint8obj2 { return compareGreater, true } @@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint16: { - uint16obj1 := obj1.(uint16) - uint16obj2 := obj2.(uint16) + uint16obj1, ok := obj1.(uint16) + if !ok { + uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) + } + uint16obj2, ok := obj2.(uint16) + if !ok { + uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) + } if uint16obj1 > uint16obj2 { return compareGreater, true } @@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint32: { - uint32obj1 := obj1.(uint32) - uint32obj2 := obj2.(uint32) + uint32obj1, ok := obj1.(uint32) + if !ok { + uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) + } + uint32obj2, ok := obj2.(uint32) + if !ok { + uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) + } if uint32obj1 > uint32obj2 { return compareGreater, true } @@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Uint64: { - uint64obj1 := obj1.(uint64) - uint64obj2 := obj2.(uint64) + uint64obj1, ok := obj1.(uint64) + if !ok { + uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) + } + uint64obj2, ok := obj2.(uint64) + if !ok { + uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) + } if uint64obj1 > uint64obj2 { return compareGreater, true } @@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Float32: { - float32obj1 := obj1.(float32) - float32obj2 := obj2.(float32) + float32obj1, ok := obj1.(float32) + if !ok { + float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) + } + float32obj2, ok := obj2.(float32) + if !ok { + float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) + } if float32obj1 > float32obj2 { return compareGreater, true } @@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.Float64: { - float64obj1 := obj1.(float64) - float64obj2 := obj2.(float64) + float64obj1, ok := obj1.(float64) + if !ok { + float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) + } + float64obj2, ok := obj2.(float64) + if !ok { + float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) + } if float64obj1 > float64obj2 { return compareGreater, true } @@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { } case reflect.String: { - stringobj1 := obj1.(string) - stringobj2 := obj2.(string) + stringobj1, ok := obj1.(string) + if !ok { + stringobj1 = obj1Value.Convert(stringType).Interface().(string) + } + stringobj2, ok := obj2.(string) + if !ok { + stringobj2 = obj2Value.Convert(stringType).Interface().(string) + } if stringobj1 > stringobj2 { return compareGreater, true } @@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) } +// Positive asserts that the specified element is positive +// +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) +func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) +} + +// Negative asserts that the specified element is negative +// +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) +func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + zero := reflect.Zero(reflect.TypeOf(e)) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) +} + func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 49370eb1..4dfd1229 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { return Error(t, err, append([]interface{}{msg}, args...)...) } +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // @@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } +// IsDecreasingf asserts that the collection is decreasing +// +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) +} + // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) } +// Negativef asserts that the specified element is negative +// +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") +func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Negative(t, e, append([]interface{}{msg}, args...)...) +} + // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) +} + // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") @@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) } +// Positivef asserts that the specified element is positive +// +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") +func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Positive(t, e, append([]interface{}{msg}, args...)...) +} + // Regexpf asserts that a specified regexp matches a string. // // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 9db88942..25337a6f 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { return Error(a.t, err, msgAndArgs...) } +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAs(a.t, err, target, msgAndArgs...) +} + +// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorAsf(a.t, err, target, msg, args...) +} + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIs(a.t, err, target, msgAndArgs...) +} + +// ErrorIsf asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorIsf(a.t, err, target, msg, args...) +} + // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() @@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } +// IsDecreasing asserts that the collection is decreasing +// +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) +func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasing(a.t, object, msgAndArgs...) +} + +// IsDecreasingf asserts that the collection is decreasing +// +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsDecreasingf(a.t, object, msg, args...) +} + +// IsIncreasing asserts that the collection is increasing +// +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) +func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasing(a.t, object, msgAndArgs...) +} + +// IsIncreasingf asserts that the collection is increasing +// +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsIncreasingf(a.t, object, msg, args...) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) +func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasing(a.t, object, msgAndArgs...) +} + +// IsNonDecreasingf asserts that the collection is not decreasing +// +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonDecreasingf(a.t, object, msg, args...) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) +func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasing(a.t, object, msgAndArgs...) +} + +// IsNonIncreasingf asserts that the collection is not increasing +// +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNonIncreasingf(a.t, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i return Lessf(a.t, e1, e2, msg, args...) } +// Negative asserts that the specified element is negative +// +// a.Negative(-1) +// a.Negative(-1.23) +func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negative(a.t, e, msgAndArgs...) +} + +// Negativef asserts that the specified element is negative +// +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") +func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Negativef(a.t, e, msg, args...) +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // @@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIs(a.t, err, target, msgAndArgs...) +} + +// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorIsf(a.t, err, target, msg, args...) +} + // NotNil asserts that the specified object is not nil. // // a.NotNil(err) @@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b return Panicsf(a.t, f, msg, args...) } +// Positive asserts that the specified element is positive +// +// a.Positive(1) +// a.Positive(1.23) +func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positive(a.t, e, msgAndArgs...) +} + +// Positivef asserts that the specified element is positive +// +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") +func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Positivef(a.t, e, msg, args...) +} + // Regexp asserts that a specified regexp matches a string. // // a.Regexp(regexp.MustCompile("start"), "it's starting") diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go new file mode 100644 index 00000000..1c3b4718 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -0,0 +1,81 @@ +package assert + +import ( + "fmt" + "reflect" +) + +// isOrdered checks that collection contains orderable elements. +func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { + objKind := reflect.TypeOf(object).Kind() + if objKind != reflect.Slice && objKind != reflect.Array { + return false + } + + objValue := reflect.ValueOf(object) + objLen := objValue.Len() + + if objLen <= 1 { + return true + } + + value := objValue.Index(0) + valueInterface := value.Interface() + firstValueKind := value.Kind() + + for i := 1; i < objLen; i++ { + prevValue := value + prevValueInterface := valueInterface + + value = objValue.Index(i) + valueInterface = value.Interface() + + compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) + + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + } + + if !containsValue(allowedComparesResults, compareResult) { + return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) + } + } + + return true +} + +// IsIncreasing asserts that the collection is increasing +// +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) +func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) +} + +// IsNonIncreasing asserts that the collection is not increasing +// +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) +func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) +} + +// IsDecreasing asserts that the collection is decreasing +// +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) +func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) +} + +// IsNonDecreasing asserts that the collection is not decreasing +// +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) +func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 914a10d8..bcac4401 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -172,8 +172,8 @@ func isTest(name, prefix string) bool { if len(name) == len(prefix) { // "Test" is ok return true } - rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(rune) + r, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(r) } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { @@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{ DisableCapacities: true, SortKeys: true, DisableMethods: true, + MaxDepth: 10, } type tHelper interface { @@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D } } } + +// ErrorIs asserts that at least one of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ + "expected: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// NotErrorIs asserts that at none of the errors in err's chain matches target. +// This is a wrapper for errors.Is. +func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.Is(err, target) { + return true + } + + var expectedText string + if target != nil { + expectedText = target.Error() + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", expectedText, chain, + ), msgAndArgs...) +} + +// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. +// This is a wrapper for errors.As. +func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ + "expected: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + +func buildErrorChainString(err error) string { + if err == nil { + return "" + } + + e := errors.Unwrap(err) + chain := fmt.Sprintf("%q", err.Error()) + for e != nil { + chain += fmt.Sprintf("\n\t%q", e.Error()) + e = errors.Unwrap(e) + } + return chain +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 70f06944..fa10b856 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,6 +1,6 @@ # cloud.google.com/go v0.49.0 cloud.google.com/go/compute/metadata -# github.com/1Password/connect-sdk-go v1.0.1 +# github.com/1Password/connect-sdk-go v1.2.0 github.com/1Password/connect-sdk-go/connect github.com/1Password/connect-sdk-go/onepassword # github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503 @@ -110,7 +110,7 @@ github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag -# github.com/stretchr/testify v1.6.1 +# github.com/stretchr/testify v1.7.0 github.com/stretchr/testify/assert # github.com/uber/jaeger-client-go v2.25.0+incompatible github.com/uber/jaeger-client-go