826d9d47f9
The CSI HTTP API has to transform the CSI volume to redact secrets, remove the claims fields, and to consolidate the allocation stubs into a single slice of alloc stubs. This was done manually in #8590 but this is a large amount of code and has proven both very bug prone (see #8659, #8666, #8699, #8735, and #12150) and requires updating lots of code every time we add a field to volumes or plugins. In #10202 we introduce encoding improvements for the `Node` struct that allow a more minimal transformation. Apply this same approach to serializing `structs.CSIVolume` to API responses. Also, the original reasoning behind #8590 for plugins no longer holds because the counts are now denormalized within the state store, so we can simply remove this transformation entirely.
79 lines
2.5 KiB
Go
79 lines
2.5 KiB
Go
package structs
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
var (
|
|
// extendedTypes is a mapping of extended types to their extension function
|
|
// TODO: the duplicates could be simplified by looking up the base type in the case of a pointer type in ConvertExt
|
|
extendedTypes = map[reflect.Type]extendFunc{
|
|
reflect.TypeOf(Node{}): nodeExt,
|
|
reflect.TypeOf(&Node{}): nodeExt,
|
|
reflect.TypeOf(CSIVolume{}): csiVolumeExt,
|
|
reflect.TypeOf(&CSIVolume{}): csiVolumeExt,
|
|
}
|
|
)
|
|
|
|
// nodeExt ensures the node is sanitized and adds the legacy field .Drain back to encoded Node objects
|
|
func nodeExt(v interface{}) interface{} {
|
|
node := v.(*Node).Sanitize()
|
|
// transform to a struct with inlined Node fields plus the Drain field
|
|
// - using defined type (not an alias!) EmbeddedNode gives us free conversion to a distinct type
|
|
// - distinct type prevents this encoding extension from being called recursively/infinitely on the embedding
|
|
// - pointers mean the conversion function doesn't have to make a copy during conversion
|
|
type EmbeddedNode Node
|
|
return &struct {
|
|
*EmbeddedNode
|
|
Drain bool
|
|
}{
|
|
EmbeddedNode: (*EmbeddedNode)(node),
|
|
Drain: node != nil && node.DrainStrategy != nil,
|
|
}
|
|
}
|
|
|
|
func csiVolumeExt(v interface{}) interface{} {
|
|
vol := v.(*CSIVolume)
|
|
type EmbeddedCSIVolume CSIVolume
|
|
|
|
allocCount := len(vol.ReadAllocs) + len(vol.WriteAllocs)
|
|
|
|
apiVol := &struct {
|
|
*EmbeddedCSIVolume
|
|
Allocations []*AllocListStub
|
|
}{
|
|
EmbeddedCSIVolume: (*EmbeddedCSIVolume)(vol),
|
|
Allocations: make([]*AllocListStub, 0, allocCount),
|
|
}
|
|
|
|
// WriteAllocs and ReadAllocs will only ever contain the Allocation ID,
|
|
// with a null value for the Allocation; these IDs are mapped to
|
|
// allocation stubs in the Allocations field. This indirection is so the
|
|
// API can support both the UI and CLI consumer in a safely backwards
|
|
// compatible way
|
|
for _, a := range vol.ReadAllocs {
|
|
if a != nil {
|
|
apiVol.ReadAllocs[a.ID] = nil
|
|
apiVol.Allocations = append(apiVol.Allocations, a.Stub(nil))
|
|
}
|
|
}
|
|
for _, a := range vol.WriteAllocs {
|
|
if a != nil {
|
|
apiVol.WriteAllocs[a.ID] = nil
|
|
apiVol.Allocations = append(apiVol.Allocations, a.Stub(nil))
|
|
}
|
|
}
|
|
|
|
// MountFlags can contain secrets, so we always redact it but want
|
|
// to show the user that we have the value
|
|
if vol.MountOptions != nil && len(vol.MountOptions.MountFlags) > 0 {
|
|
apiVol.MountOptions.MountFlags = []string{"[REDACTED]"}
|
|
}
|
|
|
|
// would be better not to have at all but left in and redacted for
|
|
// backwards compatibility with the existing API
|
|
apiVol.Secrets = nil
|
|
|
|
return apiVol
|
|
}
|