| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- package kubecost
- import (
- "fmt"
- "sort"
- "strings"
- util "github.com/kubecost/cost-model/pkg/util"
- )
- type Property string
- const (
- NilProp Property = ""
- ClusterProp Property = "cluster"
- NodeProp Property = "node"
- ContainerProp Property = "container"
- ControllerProp Property = "controller"
- ControllerKindProp Property = "controllerKind"
- LabelProp Property = "label"
- AnnotationProp Property = "annotation"
- NamespaceProp Property = "namespace"
- PodProp Property = "pod"
- ServiceProp Property = "service"
- )
- var availableProperties []Property = []Property{
- NilProp,
- ClusterProp,
- NodeProp,
- ContainerProp,
- ControllerProp,
- ControllerKindProp,
- LabelProp,
- AnnotationProp,
- NamespaceProp,
- PodProp,
- ServiceProp,
- }
- func ParseProperty(prop string) Property {
- for _, property := range availableProperties {
- if strings.ToLower(string(property)) == strings.ToLower(prop) {
- return property
- }
- }
- return NilProp
- }
- func (p Property) String() string {
- return string(p)
- }
- type PropertyValue struct {
- Property Property
- Value interface{}
- }
- // Properties describes a set of Kubernetes objects.
- type Properties map[Property]interface{}
- // TODO niko/etl make sure Services deep copy works correctly
- func (p *Properties) Clone() Properties {
- if p == nil {
- return nil
- }
- clone := make(Properties, len(*p))
- for k, v := range *p {
- clone[k] = v
- }
- return clone
- }
- func (p *Properties) Equal(that *Properties) bool {
- if p == nil || that == nil {
- return false
- }
- if p.Length() != that.Length() {
- return false
- }
- pCluster, _ := p.GetCluster()
- thatCluster, _ := that.GetCluster()
- if pCluster != thatCluster {
- return false
- }
- pNode, _ := p.GetNode()
- thatNode, _ := that.GetNode()
- if pNode != thatNode {
- return false
- }
- pContainer, _ := p.GetContainer()
- thatContainer, _ := that.GetContainer()
- if pContainer != thatContainer {
- return false
- }
- pController, _ := p.GetController()
- thatController, _ := that.GetController()
- if pController != thatController {
- return false
- }
- pControllerKind, _ := p.GetControllerKind()
- thatControllerKind, _ := that.GetControllerKind()
- if pControllerKind != thatControllerKind {
- return false
- }
- pNamespace, _ := p.GetNamespace()
- thatNamespace, _ := that.GetNamespace()
- if pNamespace != thatNamespace {
- return false
- }
- pPod, _ := p.GetPod()
- thatPod, _ := that.GetPod()
- if pPod != thatPod {
- return false
- }
- pLabels, _ := p.GetLabels()
- thatLabels, _ := that.GetLabels()
- if len(pLabels) != len(thatLabels) {
- for k, pv := range pLabels {
- tv, ok := thatLabels[k]
- if !ok || tv != pv {
- return false
- }
- }
- return false
- }
- pAnnotations, _ := p.GetAnnotations()
- thatAnnotations, _ := that.GetAnnotations()
- if len(pAnnotations) != len(thatAnnotations) {
- for k, pv := range pAnnotations {
- tv, ok := thatAnnotations[k]
- if !ok || tv != pv {
- return false
- }
- }
- return false
- }
- pServices, _ := p.GetServices()
- thatServices, _ := that.GetServices()
- if len(pServices) != len(thatServices) {
- sort.Strings(pServices)
- sort.Strings(thatServices)
- for i, pv := range pServices {
- tv := thatServices[i]
- if tv != pv {
- return false
- }
- }
- return false
- }
- return true
- }
- func (p *Properties) Intersection(that Properties) Properties {
- spec := &Properties{}
- sCluster, sErr := p.GetCluster()
- tCluster, tErr := that.GetCluster()
- if sErr == nil && tErr == nil && sCluster == tCluster {
- spec.SetCluster(sCluster)
- }
- sNode, sErr := p.GetNode()
- tNode, tErr := that.GetNode()
- if sErr == nil && tErr == nil && sNode == tNode {
- spec.SetNode(sNode)
- }
- sContainer, sErr := p.GetContainer()
- tContainer, tErr := that.GetContainer()
- if sErr == nil && tErr == nil && sContainer == tContainer {
- spec.SetContainer(sContainer)
- }
- sController, sErr := p.GetController()
- tController, tErr := that.GetController()
- if sErr == nil && tErr == nil && sController == tController {
- spec.SetController(sController)
- }
- sControllerKind, sErr := p.GetControllerKind()
- tControllerKind, tErr := that.GetControllerKind()
- if sErr == nil && tErr == nil && sControllerKind == tControllerKind {
- spec.SetControllerKind(sControllerKind)
- }
- sNamespace, sErr := p.GetNamespace()
- tNamespace, tErr := that.GetNamespace()
- if sErr == nil && tErr == nil && sNamespace == tNamespace {
- spec.SetNamespace(sNamespace)
- }
- sPod, sErr := p.GetPod()
- tPod, tErr := that.GetPod()
- if sErr == nil && tErr == nil && sPod == tPod {
- spec.SetPod(sPod)
- }
- // TODO niko/etl intersection of services and labels and annotations
- return *spec
- }
- // Length returns the number of Properties
- func (p *Properties) Length() int {
- if p == nil {
- return 0
- }
- return len(*p)
- }
- func (p *Properties) String() string {
- if p == nil {
- return "<nil>"
- }
- strs := []string{}
- for key, prop := range *p {
- strs = append(strs, fmt.Sprintf("%s:%s", key, prop))
- }
- return fmt.Sprintf("{%s}", strings.Join(strs, "; "))
- }
- // AggregationStrings converts a Properties object into a slice of strings
- // representing a request to aggregate by certain properties.
- // NOTE: today, the ordering of the properties *has to match the ordering
- // of the allocaiton function generateKey*
- func (p *Properties) AggregationStrings() []string {
- if p == nil {
- return []string{}
- }
- aggStrs := []string{}
- if p.HasCluster() {
- aggStrs = append(aggStrs, ClusterProp.String())
- }
- if p.HasNode() {
- aggStrs = append(aggStrs, NodeProp.String())
- }
- if p.HasNamespace() {
- aggStrs = append(aggStrs, NamespaceProp.String())
- }
- if p.HasControllerKind() {
- aggStrs = append(aggStrs, ControllerKindProp.String())
- }
- if p.HasController() {
- aggStrs = append(aggStrs, ControllerProp.String())
- }
- if p.HasPod() {
- aggStrs = append(aggStrs, PodProp.String())
- }
- if p.HasContainer() {
- aggStrs = append(aggStrs, ContainerProp.String())
- }
- if p.HasService() {
- aggStrs = append(aggStrs, ServiceProp.String())
- }
- if p.HasLabel() {
- // e.g. expect format map[string]string{
- // "env":""
- // "app":"",
- // }
- // for aggregating by "label:app,label:env"
- labels, _ := p.GetLabels()
- labelAggStrs := []string{}
- for labelName := range labels {
- labelAggStrs = append(labelAggStrs, fmt.Sprintf("label:%s", labelName))
- }
- if len(labelAggStrs) > 0 {
- // Enforce alphabetical ordering, then append to aggStrs
- sort.Strings(labelAggStrs)
- for _, labelName := range labelAggStrs {
- aggStrs = append(aggStrs, labelName)
- }
- }
- }
- return aggStrs
- }
- func (p *Properties) Get(prop Property) (string, error) {
- if raw, ok := (*p)[prop]; ok {
- if result, ok := raw.(string); ok {
- return result, nil
- }
- return "", fmt.Errorf("%s is not a string", prop)
- }
- return "", fmt.Errorf("%s not set", prop)
- }
- func (p *Properties) Has(prop Property) bool {
- _, ok := (*p)[prop]
- return ok
- }
- func (p *Properties) Set(prop Property, value string) {
- (*p)[prop] = value
- }
- func (p *Properties) GetCluster() (string, error) {
- if raw, ok := (*p)[ClusterProp]; ok {
- if cluster, ok := raw.(string); ok {
- return cluster, nil
- }
- return "", fmt.Errorf("ClusterProp is not a string")
- }
- return "", fmt.Errorf("ClusterProp not set")
- }
- func (p *Properties) HasCluster() bool {
- _, ok := (*p)[ClusterProp]
- return ok
- }
- func (p *Properties) SetCluster(cluster string) {
- (*p)[ClusterProp] = cluster
- }
- func (p *Properties) GetNode() (string, error) {
- if raw, ok := (*p)[NodeProp]; ok {
- if node, ok := raw.(string); ok {
- return node, nil
- }
- return "", fmt.Errorf("NodeProp is not a string")
- }
- return "", fmt.Errorf("NodeProp not set")
- }
- func (p *Properties) HasNode() bool {
- _, ok := (*p)[NodeProp]
- return ok
- }
- func (p *Properties) SetNode(node string) {
- (*p)[NodeProp] = node
- }
- func (p *Properties) GetContainer() (string, error) {
- if raw, ok := (*p)[ContainerProp]; ok {
- if container, ok := raw.(string); ok {
- return container, nil
- }
- return "", fmt.Errorf("ContainerProp is not a string")
- }
- return "", fmt.Errorf("ContainerProp not set")
- }
- func (p *Properties) HasContainer() bool {
- _, ok := (*p)[ContainerProp]
- return ok
- }
- func (p *Properties) SetContainer(container string) {
- (*p)[ContainerProp] = container
- }
- func (p *Properties) GetController() (string, error) {
- if raw, ok := (*p)[ControllerProp]; ok {
- if controller, ok := raw.(string); ok {
- return controller, nil
- }
- return "", fmt.Errorf("ControllerProp is not a string")
- }
- return "", fmt.Errorf("ControllerProp not set")
- }
- func (p *Properties) HasController() bool {
- _, ok := (*p)[ControllerProp]
- return ok
- }
- func (p *Properties) SetController(controller string) {
- (*p)[ControllerProp] = controller
- }
- func (p *Properties) GetControllerKind() (string, error) {
- if raw, ok := (*p)[ControllerKindProp]; ok {
- if controllerKind, ok := raw.(string); ok {
- return controllerKind, nil
- }
- return "", fmt.Errorf("ControllerKindProp is not a string")
- }
- return "", fmt.Errorf("ControllerKindProp not set")
- }
- func (p *Properties) HasControllerKind() bool {
- _, ok := (*p)[ControllerKindProp]
- return ok
- }
- func (p *Properties) SetControllerKind(controllerKind string) {
- (*p)[ControllerKindProp] = controllerKind
- }
- func (p *Properties) GetLabels() (map[string]string, error) {
- if raw, ok := (*p)[LabelProp]; ok {
- if labels, ok := raw.(map[string]string); ok {
- return labels, nil
- }
- return map[string]string{}, fmt.Errorf("LabelProp is not a map[string]string")
- }
- return map[string]string{}, fmt.Errorf("LabelProp not set")
- }
- func (p *Properties) HasLabel() bool {
- _, ok := (*p)[LabelProp]
- return ok
- }
- func (p *Properties) SetLabels(labels map[string]string) {
- (*p)[LabelProp] = labels
- }
- func (p *Properties) GetAnnotations() (map[string]string, error) {
- if raw, ok := (*p)[AnnotationProp]; ok {
- if annotations, ok := raw.(map[string]string); ok {
- return annotations, nil
- }
- return map[string]string{}, fmt.Errorf("AnnotationProp is not a map[string]string")
- }
- return map[string]string{}, fmt.Errorf("AnnotationProp not set")
- }
- func (p *Properties) HasAnnotations() bool {
- _, ok := (*p)[AnnotationProp]
- return ok
- }
- func (p *Properties) SetAnnotations(annotations map[string]string) {
- (*p)[AnnotationProp] = annotations
- }
- func (p *Properties) GetNamespace() (string, error) {
- if raw, ok := (*p)[NamespaceProp]; ok {
- if namespace, ok := raw.(string); ok {
- return namespace, nil
- }
- return "", fmt.Errorf("NamespaceProp is not a string")
- }
- return "", fmt.Errorf("NamespaceProp not set")
- }
- func (p *Properties) HasNamespace() bool {
- _, ok := (*p)[NamespaceProp]
- return ok
- }
- func (p *Properties) SetNamespace(namespace string) {
- (*p)[NamespaceProp] = namespace
- }
- func (p *Properties) GetPod() (string, error) {
- if raw, ok := (*p)[PodProp]; ok {
- if pod, ok := raw.(string); ok {
- return pod, nil
- }
- return "", fmt.Errorf("PodProp is not a string")
- }
- return "", fmt.Errorf("PodProp not set")
- }
- func (p *Properties) HasPod() bool {
- _, ok := (*p)[PodProp]
- return ok
- }
- func (p *Properties) SetPod(pod string) {
- (*p)[PodProp] = pod
- }
- func (p *Properties) GetServices() ([]string, error) {
- if raw, ok := (*p)[ServiceProp]; ok {
- if services, ok := raw.([]string); ok {
- return services, nil
- }
- return []string{}, fmt.Errorf("ServiceProp is not a string")
- }
- return []string{}, fmt.Errorf("ServiceProp not set")
- }
- func (p *Properties) HasService() bool {
- _, ok := (*p)[ServiceProp]
- return ok
- }
- func (p *Properties) SetServices(services []string) {
- (*p)[ServiceProp] = services
- }
- func (p *Properties) MarshalBinary() (data []byte, err error) {
- buff := util.NewBuffer()
- buff.WriteUInt8(CodecVersion) // version
- // ClusterProp
- cluster, err := p.GetCluster()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(cluster) // write string
- }
- // NodeProp
- node, err := p.GetNode()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(node) // write string
- }
- // ContainerProp
- container, err := p.GetContainer()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(container) // write string
- }
- // ControllerProp
- controller, err := p.GetController()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(controller) // write string
- }
- // ControllerKindProp
- controllerKind, err := p.GetControllerKind()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(controllerKind) // write string
- }
- // NamespaceProp
- namespace, err := p.GetNamespace()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(namespace) // write string
- }
- // PodProp
- pod, err := p.GetPod()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteString(pod) // write string
- }
- // LabelProp
- labels, err := p.GetLabels()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteInt(len(labels)) // map length
- for k, v := range labels {
- buff.WriteString(k) // write string
- buff.WriteString(v) // write string
- }
- }
- // AnnotationProp
- annotations, err := p.GetAnnotations()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteInt(len(annotations)) // map length
- for k, v := range annotations {
- buff.WriteString(k) // write string
- buff.WriteString(v) // write string
- }
- }
- // ServiceProp
- services, err := p.GetServices()
- if err != nil {
- buff.WriteUInt8(uint8(0)) // write nil byte
- } else {
- buff.WriteUInt8(uint8(1)) // write non-nil byte
- buff.WriteInt(len(services)) // slice length
- for _, v := range services {
- buff.WriteString(v) // write string
- }
- }
- return buff.Bytes(), nil
- }
- func (p *Properties) UnmarshalBinary(data []byte) error {
- buff := util.NewBufferFromBytes(data)
- v := buff.ReadUInt8() // version
- if v != CodecVersion {
- return fmt.Errorf("Invalid Version. Expected %d, got %d", CodecVersion, v)
- }
- *p = Properties{}
- // ClusterProp
- if buff.ReadUInt8() == 1 { // read nil byte
- cluster := buff.ReadString() // read string
- p.SetCluster(cluster)
- }
- // NodeProp
- if buff.ReadUInt8() == 1 { // read nil byte
- node := buff.ReadString() // read string
- p.SetNode(node)
- }
- // ContainerProp
- if buff.ReadUInt8() == 1 { // read nil byte
- container := buff.ReadString() // read string
- p.SetContainer(container)
- }
- // ControllerProp
- if buff.ReadUInt8() == 1 { // read nil byte
- controller := buff.ReadString() // read string
- p.SetController(controller)
- }
- // ControllerKindProp
- if buff.ReadUInt8() == 1 { // read nil byte
- controllerKind := buff.ReadString() // read string
- p.SetControllerKind(controllerKind)
- }
- // NamespaceProp
- if buff.ReadUInt8() == 1 { // read nil byte
- namespace := buff.ReadString() // read string
- p.SetNamespace(namespace)
- }
- // PodProp
- if buff.ReadUInt8() == 1 { // read nil byte
- pod := buff.ReadString() // read string
- p.SetPod(pod)
- }
- // LabelProp
- if buff.ReadUInt8() == 1 { // read nil byte
- length := buff.ReadInt() // read map len
- labels := make(map[string]string, length)
- for idx := 0; idx < length; idx++ {
- key := buff.ReadString()
- val := buff.ReadString()
- labels[key] = val
- }
- p.SetLabels(labels)
- }
- // AnnotationProp
- if buff.ReadUInt8() == 1 { // read nil byte
- length := buff.ReadInt() // read map len
- annotations := make(map[string]string, length)
- for idx := 0; idx < length; idx++ {
- key := buff.ReadString()
- val := buff.ReadString()
- annotations[key] = val
- }
- p.SetAnnotations(annotations)
- }
- // ServiceProp
- if buff.ReadUInt8() == 1 { // read nil byte
- length := buff.ReadInt() // read map len
- services := make([]string, length)
- for idx := 0; idx < length; idx++ {
- val := buff.ReadString()
- services[idx] = val
- }
- p.SetServices(services)
- }
- return nil
- }
|