mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-20 14:49:14 +08:00

Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
121 lines
4.6 KiB
Go
121 lines
4.6 KiB
Go
/*
|
|
Copyright 2022 The Kubernetes 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 (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidStartRV = errors.New("continue key is not valid: incorrect encoded start resourceVersion (version meta.k8s.io/v1)")
|
|
ErrEmptyStartKey = errors.New("continue key is not valid: encoded start key empty (version meta.k8s.io/v1)")
|
|
ErrGenericInvalidKey = errors.New("continue key is not valid")
|
|
ErrUnrecognizedEncodedVersion = errors.New("continue key is not valid: server does not recognize this encoded version")
|
|
)
|
|
|
|
// continueToken is a simple structured object for encoding the state of a continue token.
|
|
// TODO: if we change the version of the encoded from, we can't start encoding the new version
|
|
// until all other servers are upgraded (i.e. we need to support rolling schema)
|
|
// This is a public API struct and cannot change.
|
|
type continueToken struct {
|
|
APIVersion string `json:"v"`
|
|
ResourceVersion int64 `json:"rv"`
|
|
StartKey string `json:"start"`
|
|
}
|
|
|
|
// DecodeContinue transforms an encoded predicate from into a versioned struct.
|
|
// TODO: return a typed error that instructs clients that they must relist
|
|
func DecodeContinue(continueValue, keyPrefix string) (fromKey string, rv int64, err error) {
|
|
data, err := base64.RawURLEncoding.DecodeString(continueValue)
|
|
if err != nil {
|
|
return "", 0, fmt.Errorf("%w: %v", ErrGenericInvalidKey, err)
|
|
}
|
|
var c continueToken
|
|
if err := json.Unmarshal(data, &c); err != nil {
|
|
return "", 0, fmt.Errorf("%w: %v", ErrGenericInvalidKey, err)
|
|
}
|
|
switch c.APIVersion {
|
|
case "meta.k8s.io/v1":
|
|
if c.ResourceVersion == 0 {
|
|
return "", 0, ErrInvalidStartRV
|
|
}
|
|
if len(c.StartKey) == 0 {
|
|
return "", 0, ErrEmptyStartKey
|
|
}
|
|
// defend against path traversal attacks by clients - path.Clean will ensure that startKey cannot
|
|
// be at a higher level of the hierarchy, and so when we append the key prefix we will end up with
|
|
// continue start key that is fully qualified and cannot range over anything less specific than
|
|
// keyPrefix.
|
|
key := c.StartKey
|
|
if !strings.HasPrefix(key, "/") {
|
|
key = "/" + key
|
|
}
|
|
cleaned := path.Clean(key)
|
|
if cleaned != key {
|
|
return "", 0, fmt.Errorf("%w: %v", ErrGenericInvalidKey, c.StartKey)
|
|
}
|
|
return keyPrefix + cleaned[1:], c.ResourceVersion, nil
|
|
default:
|
|
return "", 0, fmt.Errorf("%w %v", ErrUnrecognizedEncodedVersion, c.APIVersion)
|
|
}
|
|
}
|
|
|
|
// EncodeContinue returns a string representing the encoded continuation of the current query.
|
|
func EncodeContinue(key, keyPrefix string, resourceVersion int64) (string, error) {
|
|
nextKey := strings.TrimPrefix(key, keyPrefix)
|
|
if nextKey == key {
|
|
return "", fmt.Errorf("unable to encode next field: the key and key prefix do not match")
|
|
}
|
|
out, err := json.Marshal(&continueToken{APIVersion: "meta.k8s.io/v1", ResourceVersion: resourceVersion, StartKey: nextKey})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return base64.RawURLEncoding.EncodeToString(out), nil
|
|
}
|
|
|
|
// PrepareContinueToken prepares optional
|
|
// parameters for retrieving additional results for a paginated request.
|
|
//
|
|
// This function sets up parameters that a client can use to fetch the remaining results
|
|
// from the server if they are available.
|
|
func PrepareContinueToken(keyLastItem, keyPrefix string, resourceVersion int64, itemsCount int64, hasMoreItems bool, opts ListOptions) (string, *int64, error) {
|
|
var remainingItemCount *int64
|
|
var continueValue string
|
|
var err error
|
|
|
|
if hasMoreItems {
|
|
// Instruct the client to begin querying from immediately after the last key.
|
|
continueValue, err = EncodeContinue(keyLastItem+"\x00", keyPrefix, resourceVersion)
|
|
if err != nil {
|
|
return "", remainingItemCount, err
|
|
}
|
|
// Etcd response counts in objects that do not match the pred.
|
|
// Instead of returning inaccurate count for non-empty selectors, we return nil.
|
|
// We only set remainingItemCount if the predicate is empty.
|
|
if opts.Predicate.Empty() {
|
|
remainingItems := itemsCount - opts.Predicate.Limit
|
|
remainingItemCount = &remainingItems
|
|
}
|
|
}
|
|
return continueValue, remainingItemCount, err
|
|
}
|