diff --git a/Makefile b/Makefile index 78af9a4..1098ac3 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,8 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) rbac:roleName=manager-role crd:allowDangerousTypes=true webhook paths="./..." output:crd:artifacts:config=config/crd/bases +## allowDangerousTypes=true for v2beta structs .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/api/v2beta1/shared_conversion.go b/api/v2beta1/shared_conversion.go index 789c075..e5507b9 100644 --- a/api/v2beta1/shared_conversion.go +++ b/api/v2beta1/shared_conversion.go @@ -2,6 +2,7 @@ package v2beta1 import ( pdoknlv3 "github.com/pdok/mapserver-operator/api/v3" + shared_model "github.com/pdok/smooth-operator/model" autoscalingv2 "k8s.io/api/autoscaling/v2beta1" corev1 "k8s.io/api/core/v1" ) @@ -10,12 +11,36 @@ func Pointer[T interface{}](val T) *T { return &val } -func PointerValWithDefault[T interface{}](ptr *T, defaultValue T) T { - if ptr == nil { - return defaultValue +func PointerVal[T interface{}](val *T, def T) T { + if val == nil { + return def + } else { + return *val } +} - return *ptr +func ConverseOptionsV2ToV3(src WMSWFSOptions) *pdoknlv3.Options { + return &pdoknlv3.Options{ + AutomaticCasing: src.AutomaticCasing, + IncludeIngress: src.IncludeIngress, + PrefetchData: src.PrefetchData, + ValidateRequests: src.ValidateRequests, + RewriteGroupToDataLayers: src.RewriteGroupToDataLayers, + DisableWebserviceProxy: src.DisableWebserviceProxy, + ValidateChildStyleNameEqual: src.ValidateChildStyleNameEqual, + } +} + +func ConverseOptionsV3ToV2(src *pdoknlv3.Options) WMSWFSOptions { + return WMSWFSOptions{ + AutomaticCasing: src.AutomaticCasing, + PrefetchData: src.PrefetchData, + IncludeIngress: src.IncludeIngress, + ValidateRequests: src.ValidateRequests, + RewriteGroupToDataLayers: src.RewriteGroupToDataLayers, + DisableWebserviceProxy: src.DisableWebserviceProxy, + ValidateChildStyleNameEqual: src.ValidateChildStyleNameEqual, + } } func ConverseAutoscaling(src Autoscaling) *autoscalingv2.HorizontalPodAutoscalerSpec { @@ -89,3 +114,128 @@ func ConverseColumnsWithAliasV3ToColumnsAndAliasesV2(columns []pdoknlv3.Columns) return v2Columns, v2Aliases } + +func ConverseV2DataToV3(v2 Data) pdoknlv3.Data { + v3 := pdoknlv3.Data{} + + if v2.GPKG != nil { + v3.Gpkg = &pdoknlv3.Gpkg{ + BlobKey: v2.GPKG.BlobKey, + TableName: v2.GPKG.Table, + GeometryType: v2.GPKG.GeometryType, + Columns: ConverseColumnAndAliasesV2ToColumnsWithAliasV3( + v2.GPKG.Columns, + v2.GPKG.Aliases, + ), + } + } + + if v2.Postgis != nil { + v3.Postgis = &pdoknlv3.Postgis{ + TableName: v2.Postgis.Table, + GeometryType: v2.Postgis.GeometryType, + Columns: ConverseColumnAndAliasesV2ToColumnsWithAliasV3( + v2.Postgis.Columns, + v2.Postgis.Aliases, + ), + } + } + + if v2.Tif != nil { + v3.TIF = &pdoknlv3.TIF{ + BlobKey: v2.Tif.BlobKey, + Resample: v2.Tif.Resample, + Offsite: v2.Tif.Offsite, + GetFeatureInfoIncludesClass: v2.Tif.GetFeatureInfoIncludesClass, + } + } + + return v3 +} + +func ConverseV3DataToV2(v3 pdoknlv3.Data) Data { + v2 := Data{} + + if v3.Gpkg != nil { + columns, aliases := ConverseColumnsWithAliasV3ToColumnsAndAliasesV2(v3.Gpkg.Columns) + v2.GPKG = &GPKG{ + BlobKey: v3.Gpkg.BlobKey, + Table: v3.Gpkg.TableName, + GeometryType: v3.Gpkg.GeometryType, + Columns: columns, + Aliases: aliases, + } + } + + if v3.Postgis != nil { + columns, aliases := ConverseColumnsWithAliasV3ToColumnsAndAliasesV2(v3.Postgis.Columns) + v2.Postgis = &Postgis{ + Table: v3.Postgis.TableName, + GeometryType: v3.Postgis.GeometryType, + Columns: columns, + Aliases: aliases, + } + } + + if v3.TIF != nil { + v2.Tif = &Tif{ + BlobKey: v3.TIF.BlobKey, + Offsite: v3.TIF.Offsite, + Resample: v3.TIF.Resample, + GetFeatureInfoIncludesClass: v3.TIF.GetFeatureInfoIncludesClass, + } + } + + return v2 +} + +func NewV2KubernetesObject(lifecycle *shared_model.Lifecycle, podSpecPatch *corev1.PodSpec, scalingSpec *autoscalingv2.HorizontalPodAutoscalerSpec) Kubernetes { + kub := Kubernetes{} + + if lifecycle != nil && lifecycle.TTLInDays != nil { + kub.Lifecycle = &Lifecycle{ + TTLInDays: Pointer(int(*lifecycle.TTLInDays)), + } + } + + // TODO - healthcheck + if podSpecPatch != nil { + kub.Resources = &podSpecPatch.Containers[0].Resources + } + + if scalingSpec != nil { + kub.Autoscaling = &Autoscaling{ + MaxReplicas: Pointer(int(scalingSpec.MaxReplicas)), + } + + if scalingSpec.MinReplicas != nil { + kub.Autoscaling.MinReplicas = Pointer(int(*scalingSpec.MinReplicas)) + } + + if scalingSpec.Metrics != nil { + kub.Autoscaling.AverageCPUUtilization = Pointer( + int(*scalingSpec.Metrics[0].Resource.TargetAverageUtilization), + ) + } + } + + return kub +} + +func LabelsToV2General(labels map[string]string) General { + general := General{ + Dataset: labels["dataset"], + DatasetOwner: labels["dataset-owner"], + DataVersion: nil, + } + + if serviceVersion, ok := labels["service-version"]; ok { + general.ServiceVersion = &serviceVersion + } + + if theme, ok := labels["theme"]; ok { + general.Theme = &theme + } + + return general +} diff --git a/api/v2beta1/wfs_conversion.go b/api/v2beta1/wfs_conversion.go index 6a4ee5b..617eb5e 100644 --- a/api/v2beta1/wfs_conversion.go +++ b/api/v2beta1/wfs_conversion.go @@ -57,13 +57,9 @@ func (src *WFS) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.PodSpecPatch = ConverseResources(*src.Spec.Kubernetes.Resources) } - dst.Spec.Options = &pdoknlv3.Options{ - AutomaticCasing: src.Spec.Options.AutomaticCasing, - PrefetchData: PointerValWithDefault(src.Spec.Options.PrefetchData, false), - IncludeIngress: src.Spec.Options.IncludeIngress, - } + dst.Spec.Options = ConverseOptionsV2ToV3(src.Spec.Options) - service := pdoknlv3.Service{ + service := pdoknlv3.WFSService{ Prefix: "", BaseURL: "https://service.pdok.nl", OwnerInfoRef: "pdok", @@ -141,28 +137,7 @@ func convertV2FeatureTypeToV3(src FeatureType) pdoknlv3.FeatureType { } } - if src.Data.GPKG != nil { - featureTypeV3.Data.Gpkg = &pdoknlv3.Gpkg{ - BlobKey: src.Data.GPKG.BlobKey, - TableName: src.Data.GPKG.Table, - GeometryType: src.Data.GPKG.GeometryType, - Columns: ConverseColumnAndAliasesV2ToColumnsWithAliasV3( - src.Data.GPKG.Columns, - src.Data.GPKG.Aliases, - ), - } - } - - if src.Data.Postgis != nil { - featureTypeV3.Data.Postgis = &pdoknlv3.Postgis{ - TableName: src.Data.Postgis.Table, - GeometryType: src.Data.Postgis.GeometryType, - Columns: ConverseColumnAndAliasesV2ToColumnsWithAliasV3( - src.Data.Postgis.Columns, - src.Data.Postgis.Aliases, - ), - } - } + featureTypeV3.Data = ConverseV2DataToV3(src.Data) return featureTypeV3 } @@ -177,55 +152,12 @@ func (dst *WFS) ConvertFrom(srcRaw conversion.Hub) error { dst.ObjectMeta = src.ObjectMeta - dst.Spec.General = General{ - Dataset: src.ObjectMeta.Labels["dataset"], - DatasetOwner: src.ObjectMeta.Labels["dataset-owner"], - DataVersion: nil, - } - - if serviceVersion, ok := src.ObjectMeta.Labels["service-version"]; ok { - dst.Spec.General.ServiceVersion = &serviceVersion - } - - if theme, ok := src.ObjectMeta.Labels["theme"]; ok { - dst.Spec.General.Theme = &theme - } - - dst.Spec.Kubernetes = Kubernetes{} - - if src.Spec.Lifecycle != nil && src.Spec.Lifecycle.TTLInDays != nil { - dst.Spec.Kubernetes.Lifecycle = &Lifecycle{ - TTLInDays: Pointer(int(*src.Spec.Lifecycle.TTLInDays)), - } - } - - // TODO - healthcheck - if src.Spec.PodSpecPatch != nil { - dst.Spec.Kubernetes.Resources = &src.Spec.PodSpecPatch.Containers[0].Resources - } - - if src.Spec.HorizontalPodAutoscalerPatch != nil { - dst.Spec.Kubernetes.Autoscaling = &Autoscaling{ - MaxReplicas: Pointer(int(src.Spec.HorizontalPodAutoscalerPatch.MaxReplicas)), - } - - if src.Spec.HorizontalPodAutoscalerPatch.MinReplicas != nil { - dst.Spec.Kubernetes.Autoscaling.MinReplicas = Pointer(int(*src.Spec.HorizontalPodAutoscalerPatch.MinReplicas)) - } + dst.Spec.General = LabelsToV2General(src.ObjectMeta.Labels) - if src.Spec.HorizontalPodAutoscalerPatch.Metrics != nil { - dst.Spec.Kubernetes.Autoscaling.AverageCPUUtilization = Pointer( - int(*src.Spec.HorizontalPodAutoscalerPatch.Metrics[0].Resource.TargetAverageUtilization), - ) - } - } + dst.Spec.Kubernetes = NewV2KubernetesObject(src.Spec.Lifecycle, src.Spec.PodSpecPatch, src.Spec.HorizontalPodAutoscalerPatch) - if src.Spec.Options == nil { - dst.Spec.Options = WMSWFSOptions{ - AutomaticCasing: src.Spec.Options.AutomaticCasing, - PrefetchData: &src.Spec.Options.PrefetchData, - IncludeIngress: src.Spec.Options.IncludeIngress, - } + if src.Spec.Options != nil { + dst.Spec.Options = ConverseOptionsV3ToV2(src.Spec.Options) } service := WFSService{ @@ -258,6 +190,7 @@ func (dst *WFS) ConvertFrom(srcRaw conversion.Hub) error { service.MetadataIdentifier = src.Spec.Service.Inspire.ServiceMetadataURL.CSW.MetadataIdentifier } else { service.Inspire = false + // TODO unable to fill in MetadataIdentifier here untill we know how to handle non inspire services } for _, featureType := range src.Spec.Service.FeatureTypes { @@ -268,7 +201,7 @@ func (dst *WFS) ConvertFrom(srcRaw conversion.Hub) error { Keywords: featureType.Keywords, DatasetMetadataIdentifier: featureType.DatasetMetadataURL.CSW.MetadataIdentifier, SourceMetadataIdentifier: "", - Data: Data{}, + Data: ConverseV3DataToV2(featureType.Data), } if src.Spec.Service.Inspire != nil { @@ -279,27 +212,6 @@ func (dst *WFS) ConvertFrom(srcRaw conversion.Hub) error { featureTypeV2.Extent = Pointer(featureType.Bbox.DefaultCRS.ToExtent()) } - if featureType.Data.Gpkg != nil { - columns, aliases := ConverseColumnsWithAliasV3ToColumnsAndAliasesV2(featureType.Data.Gpkg.Columns) - featureTypeV2.Data.GPKG = &GPKG{ - BlobKey: featureType.Data.Gpkg.BlobKey, - Table: featureType.Data.Gpkg.TableName, - GeometryType: featureType.Data.Gpkg.GeometryType, - Columns: columns, - Aliases: aliases, - } - } - - if featureType.Data.Postgis != nil { - columns, aliases := ConverseColumnsWithAliasV3ToColumnsAndAliasesV2(featureType.Data.Postgis.Columns) - featureTypeV2.Data.Postgis = &Postgis{ - Table: featureType.Data.Postgis.TableName, - GeometryType: featureType.Data.Postgis.GeometryType, - Columns: columns, - Aliases: aliases, - } - } - service.FeatureTypes = append(service.FeatureTypes, featureTypeV2) } diff --git a/api/v2beta1/wms_conversion.go b/api/v2beta1/wms_conversion.go index 9698344..1929b13 100644 --- a/api/v2beta1/wms_conversion.go +++ b/api/v2beta1/wms_conversion.go @@ -25,20 +25,107 @@ SOFTWARE. package v2beta1 import ( + "fmt" + sharedModel "github.com/pdok/smooth-operator/model" "log" + "strconv" "sigs.k8s.io/controller-runtime/pkg/conversion" pdoknlv3 "github.com/pdok/mapserver-operator/api/v3" ) +const SERVICE_METADATA_IDENTIFIER_ANNOTATION = "pdok.nl/wms-service-metadata-uuid" + // ConvertTo converts this WMS (v2beta1) to the Hub version (v3). func (src *WMS) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*pdoknlv3.WMS) log.Printf("ConvertTo: Converting WMS from Spoke version v2beta1 to Hub version v3;"+ "source: %s/%s, target: %s/%s", src.Namespace, src.Name, dst.Namespace, dst.Name) - // TODO(user): Implement conversion logic from v2beta1 to v3 + dst.ObjectMeta = src.ObjectMeta + dst.Annotations[SERVICE_METADATA_IDENTIFIER_ANNOTATION] = src.Spec.Service.MetadataIdentifier + + // Set LifeCycle if defined + if src.Spec.Kubernetes.Lifecycle != nil && src.Spec.Kubernetes.Lifecycle.TTLInDays != nil { + dst.Spec.Lifecycle = &sharedModel.Lifecycle{ + TTLInDays: Pointer(int32(*src.Spec.Kubernetes.Lifecycle.TTLInDays)), + } + } + + if src.Spec.Kubernetes.Autoscaling != nil { + dst.Spec.HorizontalPodAutoscalerPatch = ConverseAutoscaling(*src.Spec.Kubernetes.Autoscaling) + } + + // TODO converse src.Spec.Kubernetes.HealthCheck when we know what the implementation in v3 will be + if src.Spec.Kubernetes.Resources != nil { + dst.Spec.PodSpecPatch = ConverseResources(*src.Spec.Kubernetes.Resources) + } + + dst.Spec.Options = ConverseOptionsV2ToV3(src.Spec.Options) + + service := pdoknlv3.WMSService{ + BaseURL: "https://service.pdok.nl", + OwnerInfoRef: "pdok", + Title: src.Spec.Service.Title, + Abstract: src.Spec.Service.Abstract, + Keywords: src.Spec.Service.Keywords, + Fees: nil, + AccessConstraints: src.Spec.Service.AccessConstraints, + MaxSize: nil, + Resolution: nil, + DefResolution: nil, + Inspire: nil, + DataEPSG: src.Spec.Service.DataEPSG, + Layer: src.Spec.Service.MapLayersToV3(), + } + + if src.Spec.Service.Maxsize != nil { + service.MaxSize = Pointer(int32(*src.Spec.Service.Maxsize)) + } + + if src.Spec.Service.Resolution != nil { + service.Resolution = Pointer(int32(*src.Spec.Service.Resolution)) + } + + if src.Spec.Service.DefResolution != nil { + service.DefResolution = Pointer(int32(*src.Spec.Service.DefResolution)) + } + + if src.Spec.Service.Mapfile != nil { + service.Mapfile = &pdoknlv3.Mapfile{ + ConfigMapKeyRef: src.Spec.Service.Mapfile.ConfigMapKeyRef, + } + } + + if src.Spec.Service.Inspire { + service.Inspire = &pdoknlv3.Inspire{ + ServiceMetadataURL: pdoknlv3.MetadataURL{ + CSW: &pdoknlv3.Metadata{ + MetadataIdentifier: src.Spec.Service.MetadataIdentifier, + }, + }, + SpatialDatasetIdentifier: *src.Spec.Service.Layers[0].SourceMetadataIdentifier, + Language: "nl", + } + } + + if src.Spec.Service.StylingAssets != nil { + service.StylingAssets = &pdoknlv3.StylingAssets{ + BlobKeys: src.Spec.Service.StylingAssets.BlobKeys, + ConfigMapRefs: []pdoknlv3.ConfigMapRef{}, + } + + for _, cm := range src.Spec.Service.StylingAssets.ConfigMapRefs { + service.StylingAssets.ConfigMapRefs = append(service.StylingAssets.ConfigMapRefs, pdoknlv3.ConfigMapRef{ + Name: cm.Name, + Keys: cm.Keys, + }) + } + } + + dst.Spec.Service = service + return nil } @@ -50,6 +137,343 @@ func (dst *WMS) ConvertFrom(srcRaw conversion.Hub) error { log.Printf("ConvertFrom: Converting WMS from Hub version v3 to Spoke version v2beta1;"+ "source: %s/%s, target: %s/%s", src.Namespace, src.Name, dst.Namespace, dst.Name) - // TODO(user): Implement conversion logic from v3 to v2beta1 + dst.ObjectMeta = src.ObjectMeta + + dst.Spec.General = LabelsToV2General(src.ObjectMeta.Labels) + + dst.Spec.Kubernetes = NewV2KubernetesObject(src.Spec.Lifecycle, src.Spec.PodSpecPatch, src.Spec.HorizontalPodAutoscalerPatch) + + if src.Spec.Options != nil { + dst.Spec.Options = ConverseOptionsV3ToV2(src.Spec.Options) + } + + service := WMSService{ + Title: src.Spec.Service.Title, + Abstract: src.Spec.Service.Abstract, + Keywords: src.Spec.Service.Keywords, + AccessConstraints: src.Spec.Service.AccessConstraints, + Extent: nil, + DataEPSG: src.Spec.Service.DataEPSG, + Layers: []WMSLayer{}, + MetadataIdentifier: "00000000-0000-0000-0000-000000000000", + } + + if src.Spec.Service.Mapfile != nil { + service.Mapfile = &Mapfile{ + ConfigMapKeyRef: src.Spec.Service.Mapfile.ConfigMapKeyRef, + } + } + + if src.Spec.Service.Inspire != nil { + service.Inspire = true + service.MetadataIdentifier = src.Spec.Service.Inspire.ServiceMetadataURL.CSW.MetadataIdentifier + } else { + service.Inspire = false + // TODO unable to fill in MetadataIdentifier here untill we know how to handle non inspire services + } + + uuid, ok := src.Annotations[SERVICE_METADATA_IDENTIFIER_ANNOTATION] + if service.MetadataIdentifier == "00000000-0000-0000-0000-000000000000" && ok { + service.MetadataIdentifier = uuid + } + + if src.Spec.Service.DefResolution != nil { + service.DefResolution = Pointer(int(*src.Spec.Service.DefResolution)) + } + + if src.Spec.Service.Resolution != nil { + service.Resolution = Pointer(int(*src.Spec.Service.Resolution)) + } + + if src.Spec.Service.StylingAssets != nil { + service.StylingAssets = &StylingAssets{ + BlobKeys: src.Spec.Service.StylingAssets.BlobKeys, + ConfigMapRefs: []ConfigMapRef{}, + } + + for _, cm := range src.Spec.Service.StylingAssets.ConfigMapRefs { + service.StylingAssets.ConfigMapRefs = append(service.StylingAssets.ConfigMapRefs, ConfigMapRef{ + Name: cm.Name, + Keys: cm.Keys, + }) + } + } + + if v3Authority := src.GetAuthority(); v3Authority != nil { + service.Authority = Authority{ + Name: v3Authority.Name, + URL: v3Authority.URL, + } + } + + if src.Spec.Service.MaxSize != nil { + service.Maxsize = Pointer(float64(*src.Spec.Service.MaxSize)) + } + + service.Layers = mapV3LayerToV2Layers(src.Spec.Service.Layer, nil, src.Spec.Service.DataEPSG) + + // Create BBox that combines all layer bounding boxes + for _, l := range service.Layers { + if l.Extent != nil { + if service.Extent == nil { + service.Extent = l.Extent + } else { + bbox := Pointer(sharedModel.ExtentToBBox(*service.Extent)).DeepCopy() + bbox.Combine(sharedModel.ExtentToBBox(*l.Extent)) + service.Extent = Pointer(bbox.ToExtent()) + } + } + } + + dst.Spec.Service = service + return nil } + +func (v2Service WMSService) GetTopLayer() (*WMSLayer, error) { + // Only one layer defined that has data + if len(v2Service.Layers) == 1 && v2Service.Layers[0].Data != nil { + return nil, nil + } + + // If all layers are groupless there is no toplayer + allGroupless := true + for _, layer := range v2Service.Layers { + if layer.Group != nil && *layer.Group != "" { + allGroupless = false + break + } + } + if allGroupless { + return nil, nil + } + + // Some layers have groups defined. + // That means that there must be one layer without a group, that's the top layer + for _, layer := range v2Service.Layers { + if layer.Group == nil || *layer.Group == "" { + return &layer, nil + } + } + + return nil, fmt.Errorf("unable to detect the toplayer of this WMS service") +} + +func (v2Service WMSService) GetChildLayers(parent WMSLayer) ([]WMSLayer, error) { + children := make([]WMSLayer, 0) + + for _, layer := range v2Service.Layers { + if layer.Group != nil && *layer.Group == parent.Name { + children = append(children, layer) + } + } + + if len(children) == 0 { + return children, fmt.Errorf("no child layers found") + } + + return children, nil +} + +// MapLayersToV3 +func (v2Service WMSService) MapLayersToV3() pdoknlv3.Layer { + topLayer, err := v2Service.GetTopLayer() + if err != nil { + panic(err) + } + + var layer pdoknlv3.Layer + if topLayer == nil { + layer = pdoknlv3.Layer{ + Name: "wms", + Title: &v2Service.Title, + Abstract: &v2Service.Abstract, + Keywords: v2Service.Keywords, + Layers: &[]pdoknlv3.Layer{}, + } + + var childLayersV3 []pdoknlv3.Layer + for _, childLayer := range v2Service.Layers { + childLayersV3 = append(childLayersV3, childLayer.MapToV3(v2Service)) + } + layer.Layers = &childLayersV3 + } else { + layer = topLayer.MapToV3(v2Service) + } + + return layer +} + +func (v2Layer WMSLayer) MapToV3(v2Service WMSService) pdoknlv3.Layer { + layer := pdoknlv3.Layer{ + Name: v2Layer.Name, + Title: v2Layer.Title, + Abstract: v2Layer.Abstract, + Keywords: v2Layer.Keywords, + LabelNoClip: v2Layer.LabelNoClip, + Styles: []pdoknlv3.Style{}, + Layers: &[]pdoknlv3.Layer{}, + BoundingBoxes: []pdoknlv3.WMSBoundingBox{}, + MinScaleDenominator: nil, + MaxScaleDenominator: nil, + } + + if v2Layer.SourceMetadataIdentifier != nil { + layer.Authority = &pdoknlv3.Authority{ + Name: v2Service.Authority.Name, + URL: v2Service.Authority.URL, + SpatialDatasetIdentifier: *v2Layer.SourceMetadataIdentifier, + } + } + + if v2Layer.DatasetMetadataIdentifier != nil { + layer.DatasetMetadataURL = &pdoknlv3.MetadataURL{ + CSW: &pdoknlv3.Metadata{ + MetadataIdentifier: *v2Layer.DatasetMetadataIdentifier, + }, + } + } + + if v2Layer.Extent != nil { + layer.BoundingBoxes = append(layer.BoundingBoxes, pdoknlv3.WMSBoundingBox{ + CRS: v2Service.DataEPSG, + BBox: sharedModel.ExtentToBBox(*v2Layer.Extent), + }) + } else if v2Service.Extent != nil { + layer.BoundingBoxes = append(layer.BoundingBoxes, pdoknlv3.WMSBoundingBox{ + CRS: v2Service.DataEPSG, + BBox: sharedModel.ExtentToBBox(*v2Service.Extent), + }) + } + + if v2Layer.MinScale != nil { + layer.MinScaleDenominator = Pointer(strconv.FormatFloat(*v2Layer.MinScale, 'f', -1, 64)) + } + + if v2Layer.MaxScale != nil { + layer.MaxScaleDenominator = Pointer(strconv.FormatFloat(*v2Layer.MaxScale, 'f', -1, 64)) + } + + for _, style := range v2Layer.Styles { + v3Style := pdoknlv3.Style{ + Name: style.Name, + Title: style.Title, + Abstract: style.Abstract, + Visualization: style.Visualization, + } + + if style.LegendFile != nil { + v3Style.Legend = &pdoknlv3.Legend{ + BlobKey: style.LegendFile.BlobKey, + } + } + + layer.Styles = append(layer.Styles, v3Style) + } + + if v2Layer.Data != nil { + layer.Data = Pointer(ConverseV2DataToV3(*v2Layer.Data)) + } else { + childLayersV2, err := v2Service.GetChildLayers(v2Layer) + if err != nil { + panic(err) + } + + var childLayersV3 []pdoknlv3.Layer + for _, childLayer := range childLayersV2 { + childLayersV3 = append(childLayersV3, childLayer.MapToV3(v2Service)) + } + layer.Layers = &childLayersV3 + } + + return layer +} + +func mapV3LayerToV2Layers(v3Layer pdoknlv3.Layer, parent *pdoknlv3.Layer, serviceEPSG string) []WMSLayer { + var layers []WMSLayer + + if parent == nil && v3Layer.Name == "wms" { + // Default top layer, do not include in v2 layers + if v3Layer.Layers != nil { + for _, childLayer := range *v3Layer.Layers { + layers = append(layers, mapV3LayerToV2Layers(childLayer, nil, serviceEPSG)...) + } + } + } else { + v2Layer := WMSLayer{ + Name: v3Layer.Name, + Title: v3Layer.Title, + Abstract: v3Layer.Abstract, + Keywords: v3Layer.Keywords, + LabelNoClip: v3Layer.LabelNoClip, + Styles: []Style{}, + } + + v2Layer.Visible = PointerVal(v3Layer.Visible, true) + + if parent != nil { + v2Layer.Group = &parent.Name + } + + if v3Layer.DatasetMetadataURL != nil && v3Layer.DatasetMetadataURL.CSW != nil { + v2Layer.DatasetMetadataIdentifier = &v3Layer.DatasetMetadataURL.CSW.MetadataIdentifier + } + + if v3Layer.Authority != nil { + v2Layer.SourceMetadataIdentifier = &v3Layer.Authority.SpatialDatasetIdentifier + } + + for _, bb := range v3Layer.BoundingBoxes { + if bb.CRS == serviceEPSG { + v2Layer.Extent = Pointer(bb.BBox.ToExtent()) + } + } + + if v3Layer.MinScaleDenominator != nil { + val, err := strconv.ParseFloat(*v3Layer.MinScaleDenominator, 64) + if err != nil { + panic(err) + } + v2Layer.MinScale = &val + } + + if v3Layer.MaxScaleDenominator != nil { + val, err := strconv.ParseFloat(*v3Layer.MaxScaleDenominator, 64) + if err != nil { + panic(err) + } + v2Layer.MaxScale = &val + } + + for _, v3Style := range v3Layer.Styles { + v2Style := Style{ + Name: v3Style.Name, + Title: v3Style.Title, + Abstract: v3Style.Abstract, + Visualization: v3Style.Visualization, + } + + if v3Style.Legend != nil { + v2Style.LegendFile = &LegendFile{ + BlobKey: v3Style.Legend.BlobKey, + } + } + + v2Layer.Styles = append(v2Layer.Styles, v2Style) + } + + if v3Layer.Data != nil { + v2Layer.Data = Pointer(ConverseV3DataToV2(*v3Layer.Data)) + } + + layers = append(layers, v2Layer) + + if v3Layer.Layers != nil { + for _, childLayer := range *v3Layer.Layers { + layers = append(layers, mapV3LayerToV2Layers(childLayer, &v3Layer, serviceEPSG)...) + } + } + } + + return layers +} diff --git a/api/v2beta1/wms_types.go b/api/v2beta1/wms_types.go index c44f9d8..14e0f9f 100644 --- a/api/v2beta1/wms_types.go +++ b/api/v2beta1/wms_types.go @@ -54,22 +54,21 @@ type WMSSpec struct { // WMSService is the struct for all service level fields type WMSService struct { - Inspire bool `json:"inspire"` - Title string `json:"title"` - Abstract string `json:"abstract"` - AccessConstraints string `json:"accessConstraints"` - Keywords []string `json:"keywords"` - MetadataIdentifier string `json:"metadataIdentifier"` - Authority Authority `json:"authority"` - Layers []WMSLayer `json:"layers"` - //nolint:tagliatelle - DataEPSG string `json:"dataEPSG"` - Extent *string `json:"extent,omitempty"` - Maxsize *string `json:"maxSize,omitempty"` - Resolution *int `json:"resolution,omitempty"` - DefResolution *int `json:"defResolution,omitempty"` - StylingAssets *StylingAssets `json:"stylingAssets,omitempty"` - Mapfile *Mapfile `json:"mapfile,omitempty"` + Inspire bool `json:"inspire"` + Title string `json:"title"` + Abstract string `json:"abstract"` + AccessConstraints string `json:"accessConstraints"` + Keywords []string `json:"keywords"` + MetadataIdentifier string `json:"metadataIdentifier"` + Authority Authority `json:"authority"` + Layers []WMSLayer `json:"layers"` + DataEPSG string `json:"dataEPSG"` + Extent *string `json:"extent,omitempty"` + Maxsize *float64 `json:"maxSize,omitempty"` + Resolution *int `json:"resolution,omitempty"` + DefResolution *int `json:"defResolution,omitempty"` + StylingAssets *StylingAssets `json:"stylingAssets,omitempty"` + Mapfile *Mapfile `json:"mapfile,omitempty"` } // WMSLayer is the struct for all layer level fields @@ -84,9 +83,9 @@ type WMSLayer struct { SourceMetadataIdentifier *string `json:"sourceMetadataIdentifier,omitempty"` Styles []Style `json:"styles"` Extent *string `json:"extent,omitempty"` - MinScale *string `json:"minScale,omitempty"` - MaxScale *string `json:"maxScale,omitempty"` - LabelNoClip bool `json:"labelNoClip,omitempty"` + MinScale *float64 `json:"minScale,omitempty"` + MaxScale *float64 `json:"maxScale,omitempty"` + LabelNoClip bool `json:"labelNoClip"` Data *Data `json:"data,omitempty"` } @@ -96,7 +95,7 @@ type Style struct { Title *string `json:"title,omitempty"` Abstract *string `json:"abstract,omitempty"` Visualization *string `json:"visualization,omitempty"` - LegendFile *LegendFile `json:"legendfile,omitempty"` + LegendFile *LegendFile `json:"legendFile,omitempty"` } // LegendFile is the struct containing the location of the legendfile @@ -107,7 +106,7 @@ type LegendFile struct { // StylingAssets is the struct containing the location of styling assets type StylingAssets struct { ConfigMapRefs []ConfigMapRef `json:"configMapRefs,omitempty"` - BlobKeys []string `json:"blobKeys"` + BlobKeys []string `json:"blobKeys,omitempty"` } // ConfigMapRef contains all the config map name and all keys in that configmap that are relevant diff --git a/api/v2beta1/zz_generated.deepcopy.go b/api/v2beta1/zz_generated.deepcopy.go index 8991593..b90a26c 100644 --- a/api/v2beta1/zz_generated.deepcopy.go +++ b/api/v2beta1/zz_generated.deepcopy.go @@ -749,12 +749,12 @@ func (in *WMSLayer) DeepCopyInto(out *WMSLayer) { } if in.MinScale != nil { in, out := &in.MinScale, &out.MinScale - *out = new(string) + *out = new(float64) **out = **in } if in.MaxScale != nil { in, out := &in.MaxScale, &out.MaxScale - *out = new(string) + *out = new(float64) **out = **in } if in.Data != nil { @@ -829,7 +829,7 @@ func (in *WMSService) DeepCopyInto(out *WMSService) { } if in.Maxsize != nil { in, out := &in.Maxsize, &out.Maxsize - *out = new(string) + *out = new(float64) **out = **in } if in.Resolution != nil { diff --git a/api/v3/shared_types.go b/api/v3/shared_types.go index a13f261..f6671e6 100644 --- a/api/v3/shared_types.go +++ b/api/v3/shared_types.go @@ -1,9 +1,19 @@ package v3 +import corev1 "k8s.io/api/core/v1" + +type Mapfile struct { + ConfigMapKeyRef corev1.ConfigMapKeySelector `json:"configMapKeyRef"` +} + type Options struct { - AutomaticCasing bool `json:"automaticCasing"` - PrefetchData bool `json:"prefetchData"` - IncludeIngress bool `json:"includeIngress"` + IncludeIngress bool `json:"includeIngress"` + AutomaticCasing bool `json:"automaticCasing"` + ValidateRequests *bool `json:"validateRequests,omitempty"` + RewriteGroupToDataLayers *bool `json:"rewriteGroupToDataLayers,omitempty"` + DisableWebserviceProxy *bool `json:"disableWebserviceProxy,omitempty"` + PrefetchData *bool `json:"prefetchData,omitempty"` + ValidateChildStyleNameEqual *bool `json:"validateChildStyleNameEqual,omitempty"` } type Inspire struct { @@ -29,6 +39,7 @@ type Custom struct { type Data struct { Gpkg *Gpkg `json:"gpkg,omitempty"` Postgis *Postgis `json:"postgis,omitempty"` + TIF *TIF `json:"tif,omitempty"` } type Gpkg struct { @@ -45,6 +56,13 @@ type Postgis struct { Columns []Columns `json:"columns"` } +type TIF struct { + BlobKey string `json:"blobKey"` + Resample *string `json:"resample,omitempty"` + Offsite *string `json:"offsite,omitepty"` + GetFeatureInfoIncludesClass *bool `json:"getFeatureInfoIncludesClass,omitempty"` +} + type Columns struct { Name string `json:"name"` Alias *string `json:"alias,omitempty"` diff --git a/api/v3/wfs_types.go b/api/v3/wfs_types.go index 0d483e9..9438000 100644 --- a/api/v3/wfs_types.go +++ b/api/v3/wfs_types.go @@ -74,10 +74,10 @@ type WFSSpec struct { PodSpecPatch *corev1.PodSpec `json:"podSpecPatch,omitempty"` HorizontalPodAutoscalerPatch *autoscalingv2.HorizontalPodAutoscalerSpec `json:"horizontalPodAutoscalerPatch"` Options *Options `json:"options"` - Service Service `json:"service"` + Service WFSService `json:"service"` } -type Service struct { +type WFSService struct { Prefix string `json:"prefix"` BaseURL string `json:"baseUrl"` Inspire *Inspire `json:"inspire,omitempty"` @@ -96,10 +96,6 @@ type Service struct { FeatureTypes []FeatureType `json:"featureTypes"` } -type Mapfile struct { - ConfigMapKeyRef corev1.ConfigMapKeySelector `json:"configMapKeyRef"` -} - type Bbox struct { // EXTENT/wfs_extent in mapfile //nolint:tagliatelle diff --git a/api/v3/wms_types.go b/api/v3/wms_types.go index a550a29..9930485 100644 --- a/api/v3/wms_types.go +++ b/api/v3/wms_types.go @@ -25,7 +25,12 @@ SOFTWARE. package v3 import ( + shared_model "github.com/pdok/smooth-operator/model" + autoscalingv2 "k8s.io/api/autoscaling/v2beta1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "maps" + "slices" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -33,17 +38,90 @@ import ( // WMSSpec defines the desired state of WMS. type WMSSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + Lifecycle *shared_model.Lifecycle `json:"lifecycle"` + + // +kubebuilder:validation:Type=object + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + // Optional strategic merge patch for the pod in the deployment. E.g. to patch the resources or add extra env vars. + PodSpecPatch *corev1.PodSpec `json:"podSpecPatch,omitempty"` + HorizontalPodAutoscalerPatch *autoscalingv2.HorizontalPodAutoscalerSpec `json:"horizontalPodAutoscalerPatch"` + Options *Options `json:"options"` + Service WMSService `json:"service"` +} + +type WMSService struct { + BaseURL string `json:"baseUrl"` + Title string `json:"title"` + Abstract string `json:"abstract"` + Keywords []string `json:"keywords"` + OwnerInfoRef string `json:"ownerInfoRef"` + Fees *string `json:"fees,omitempty"` + AccessConstraints string `json:"accessConstraints"` + MaxSize *int32 `json:"maxSize,omitempty"` + Inspire *Inspire `json:"inspire,omitempty"` + DataEPSG string `json:"dataEPSG"` + Resolution *int32 `json:"resolution,omitempty"` + DefResolution *int32 `json:"defResolution,omitempty"` + StylingAssets *StylingAssets `json:"stylingAssets,omitempty"` + Mapfile *Mapfile `json:"mapfile,omitempty"` + Layer Layer `json:"layer"` +} + +type StylingAssets struct { + BlobKeys []string `json:"blobKeys"` + ConfigMapRefs []ConfigMapRef `json:"configMapRefs"` +} + +type ConfigMapRef struct { + Name string `json:"name"` + Keys []string `json:"keys,omitempty"` +} + +type Layer struct { + Name string `json:"name"` + Title *string `json:"title,omitempty"` + Abstract *string `json:"abstract,omitempty"` + Keywords []string `json:"keywords"` + BoundingBoxes []WMSBoundingBox `json:"boundingBoxes"` + Visible *bool `json:"visible,omitempty"` + Authority *Authority `json:"authority,omitempty"` + DatasetMetadataURL *MetadataURL `json:"datasetMetadataUrl,omitempty"` + MinScaleDenominator *string `json:"minscaledenominator,omitempty"` + MaxScaleDenominator *string `json:"maxscaledenominator,omitempty"` + Styles []Style `json:"styles"` + LabelNoClip bool `json:"labelNoClip"` + Data *Data `json:"data,omitempty"` + // Nested structs do not work in crd generation + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Schemaless + Layers *[]Layer `json:"layers,omitempty"` +} - // Foo is an example field of WMS. Edit wms_types.go to remove/update - Foo string `json:"foo,omitempty"` +type WMSBoundingBox struct { + CRS string `json:"crs"` + BBox shared_model.BBox `json:"bbox"` } -// WMSStatus defines the observed state of WMS. -type WMSStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file +type Authority struct { + Name string `json:"name"` + URL string `json:"url"` + SpatialDatasetIdentifier string `json:"spatialDatasetIdentifier"` +} + +type Style struct { + Name string `json:"name"` + Title *string `json:"title"` + Abstract *string `json:"abstract"` + Visualization *string `json:"visualization"` + Legend *Legend `json:"legend"` +} + +type Legend struct { + Width int32 `json:"width"` + Height int32 `json:"height"` + Format string `json:"format"` + BlobKey string `json:"blobKey"` } // +kubebuilder:object:root=true @@ -59,8 +137,8 @@ type WMS struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec WMSSpec `json:"spec,omitempty"` - Status WMSStatus `json:"status,omitempty"` + Spec WMSSpec `json:"spec,omitempty"` + Status shared_model.OperatorStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true @@ -75,3 +153,40 @@ type WMSList struct { func init() { SchemeBuilder.Register(&WMS{}, &WMSList{}) } + +func (wms *WMS) GetUniqueTiffBlobKeys() []string { + blobKeys := map[string]bool{} + + if wms.Spec.Service.Layer.Data.TIF != nil && wms.Spec.Service.Layer.Data.TIF.BlobKey != "" { + blobKeys[wms.Spec.Service.Layer.Data.TIF.BlobKey] = true + } + + if wms.Spec.Service.Layer.Layers != nil && len(*wms.Spec.Service.Layer.Layers) > 0 { + for _, layer := range *wms.Spec.Service.Layer.Layers { + if layer.Data.TIF != nil && layer.Data.TIF.BlobKey != "" { + blobKeys[layer.Data.TIF.BlobKey] = true + } + } + } + return slices.Collect(maps.Keys(blobKeys)) +} + +func (wms *WMS) GetAuthority() *Authority { + if wms.Spec.Service.Layer.Authority != nil { + return wms.Spec.Service.Layer.Authority + } else { + for _, childLayer := range *wms.Spec.Service.Layer.Layers { + if childLayer.Authority != nil { + return childLayer.Authority + } else if childLayer.Layers != nil { + for _, grandChildLayer := range *childLayer.Layers { + if grandChildLayer.Authority != nil { + return grandChildLayer.Authority + } + } + } + } + } + + return nil +} diff --git a/api/v3/zz_generated.deepcopy.go b/api/v3/zz_generated.deepcopy.go index 9a9af1f..0f5546a 100644 --- a/api/v3/zz_generated.deepcopy.go +++ b/api/v3/zz_generated.deepcopy.go @@ -35,6 +35,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Authority) DeepCopyInto(out *Authority) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authority. +func (in *Authority) DeepCopy() *Authority { + if in == nil { + return nil + } + out := new(Authority) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bbox) DeepCopyInto(out *Bbox) { *out = *in @@ -71,6 +86,26 @@ func (in *Columns) DeepCopy() *Columns { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapRef) DeepCopyInto(out *ConfigMapRef) { + *out = *in + if in.Keys != nil { + in, out := &in.Keys, &out.Keys + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapRef. +func (in *ConfigMapRef) DeepCopy() *ConfigMapRef { + if in == nil { + return nil + } + out := new(ConfigMapRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Custom) DeepCopyInto(out *Custom) { *out = *in @@ -99,6 +134,11 @@ func (in *Data) DeepCopyInto(out *Data) { *out = new(Postgis) (*in).DeepCopyInto(*out) } + if in.TIF != nil { + in, out := &in.TIF, &out.TIF + *out = new(TIF) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data. @@ -197,6 +237,104 @@ func (in *Inspire) DeepCopy() *Inspire { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Layer) DeepCopyInto(out *Layer) { + *out = *in + if in.Title != nil { + in, out := &in.Title, &out.Title + *out = new(string) + **out = **in + } + if in.Abstract != nil { + in, out := &in.Abstract, &out.Abstract + *out = new(string) + **out = **in + } + if in.Keywords != nil { + in, out := &in.Keywords, &out.Keywords + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.BoundingBoxes != nil { + in, out := &in.BoundingBoxes, &out.BoundingBoxes + *out = make([]WMSBoundingBox, len(*in)) + copy(*out, *in) + } + if in.Visible != nil { + in, out := &in.Visible, &out.Visible + *out = new(bool) + **out = **in + } + if in.Authority != nil { + in, out := &in.Authority, &out.Authority + *out = new(Authority) + **out = **in + } + if in.DatasetMetadataURL != nil { + in, out := &in.DatasetMetadataURL, &out.DatasetMetadataURL + *out = new(MetadataURL) + (*in).DeepCopyInto(*out) + } + if in.MinScaleDenominator != nil { + in, out := &in.MinScaleDenominator, &out.MinScaleDenominator + *out = new(string) + **out = **in + } + if in.MaxScaleDenominator != nil { + in, out := &in.MaxScaleDenominator, &out.MaxScaleDenominator + *out = new(string) + **out = **in + } + if in.Styles != nil { + in, out := &in.Styles, &out.Styles + *out = make([]Style, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Data != nil { + in, out := &in.Data, &out.Data + *out = new(Data) + (*in).DeepCopyInto(*out) + } + if in.Layers != nil { + in, out := &in.Layers, &out.Layers + *out = new([]Layer) + if **in != nil { + in, out := *in, *out + *out = make([]Layer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Layer. +func (in *Layer) DeepCopy() *Layer { + if in == nil { + return nil + } + out := new(Layer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Legend) DeepCopyInto(out *Legend) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Legend. +func (in *Legend) DeepCopy() *Legend { + if in == nil { + return nil + } + out := new(Legend) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Mapfile) DeepCopyInto(out *Mapfile) { *out = *in @@ -256,6 +394,31 @@ func (in *MetadataURL) DeepCopy() *MetadataURL { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Options) DeepCopyInto(out *Options) { *out = *in + if in.ValidateRequests != nil { + in, out := &in.ValidateRequests, &out.ValidateRequests + *out = new(bool) + **out = **in + } + if in.RewriteGroupToDataLayers != nil { + in, out := &in.RewriteGroupToDataLayers, &out.RewriteGroupToDataLayers + *out = new(bool) + **out = **in + } + if in.DisableWebserviceProxy != nil { + in, out := &in.DisableWebserviceProxy, &out.DisableWebserviceProxy + *out = new(bool) + **out = **in + } + if in.PrefetchData != nil { + in, out := &in.PrefetchData, &out.PrefetchData + *out = new(bool) + **out = **in + } + if in.ValidateChildStyleNameEqual != nil { + in, out := &in.ValidateChildStyleNameEqual, &out.ValidateChildStyleNameEqual + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Options. @@ -291,58 +454,93 @@ func (in *Postgis) DeepCopy() *Postgis { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Service) DeepCopyInto(out *Service) { +func (in *Style) DeepCopyInto(out *Style) { *out = *in - if in.Inspire != nil { - in, out := &in.Inspire, &out.Inspire - *out = new(Inspire) - (*in).DeepCopyInto(*out) - } - if in.Mapfile != nil { - in, out := &in.Mapfile, &out.Mapfile - *out = new(Mapfile) - (*in).DeepCopyInto(*out) + if in.Title != nil { + in, out := &in.Title, &out.Title + *out = new(string) + **out = **in } - if in.Keywords != nil { - in, out := &in.Keywords, &out.Keywords - *out = make([]string, len(*in)) - copy(*out, *in) + if in.Abstract != nil { + in, out := &in.Abstract, &out.Abstract + *out = new(string) + **out = **in } - if in.Fees != nil { - in, out := &in.Fees, &out.Fees + if in.Visualization != nil { + in, out := &in.Visualization, &out.Visualization *out = new(string) **out = **in } - if in.OtherCrs != nil { - in, out := &in.OtherCrs, &out.OtherCrs + if in.Legend != nil { + in, out := &in.Legend, &out.Legend + *out = new(Legend) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Style. +func (in *Style) DeepCopy() *Style { + if in == nil { + return nil + } + out := new(Style) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StylingAssets) DeepCopyInto(out *StylingAssets) { + *out = *in + if in.BlobKeys != nil { + in, out := &in.BlobKeys, &out.BlobKeys *out = make([]string, len(*in)) copy(*out, *in) } - if in.Bbox != nil { - in, out := &in.Bbox, &out.Bbox - *out = new(Bbox) + if in.ConfigMapRefs != nil { + in, out := &in.ConfigMapRefs, &out.ConfigMapRefs + *out = make([]ConfigMapRef, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StylingAssets. +func (in *StylingAssets) DeepCopy() *StylingAssets { + if in == nil { + return nil + } + out := new(StylingAssets) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TIF) DeepCopyInto(out *TIF) { + *out = *in + if in.Resample != nil { + in, out := &in.Resample, &out.Resample + *out = new(string) **out = **in } - if in.CountDefault != nil { - in, out := &in.CountDefault, &out.CountDefault + if in.Offsite != nil { + in, out := &in.Offsite, &out.Offsite *out = new(string) **out = **in } - if in.FeatureTypes != nil { - in, out := &in.FeatureTypes, &out.FeatureTypes - *out = make([]FeatureType, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.GetFeatureInfoIncludesClass != nil { + in, out := &in.GetFeatureInfoIncludesClass, &out.GetFeatureInfoIncludesClass + *out = new(bool) + **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service. -func (in *Service) DeepCopy() *Service { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TIF. +func (in *TIF) DeepCopy() *TIF { if in == nil { return nil } - out := new(Service) + out := new(TIF) in.DeepCopyInto(out) return out } @@ -406,6 +604,63 @@ func (in *WFSList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WFSService) DeepCopyInto(out *WFSService) { + *out = *in + if in.Inspire != nil { + in, out := &in.Inspire, &out.Inspire + *out = new(Inspire) + (*in).DeepCopyInto(*out) + } + if in.Mapfile != nil { + in, out := &in.Mapfile, &out.Mapfile + *out = new(Mapfile) + (*in).DeepCopyInto(*out) + } + if in.Keywords != nil { + in, out := &in.Keywords, &out.Keywords + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Fees != nil { + in, out := &in.Fees, &out.Fees + *out = new(string) + **out = **in + } + if in.OtherCrs != nil { + in, out := &in.OtherCrs, &out.OtherCrs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Bbox != nil { + in, out := &in.Bbox, &out.Bbox + *out = new(Bbox) + **out = **in + } + if in.CountDefault != nil { + in, out := &in.CountDefault, &out.CountDefault + *out = new(string) + **out = **in + } + if in.FeatureTypes != nil { + in, out := &in.FeatureTypes, &out.FeatureTypes + *out = make([]FeatureType, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WFSService. +func (in *WFSService) DeepCopy() *WFSService { + if in == nil { + return nil + } + out := new(WFSService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WFSSpec) DeepCopyInto(out *WFSSpec) { *out = *in @@ -427,7 +682,7 @@ func (in *WFSSpec) DeepCopyInto(out *WFSSpec) { if in.Options != nil { in, out := &in.Options, &out.Options *out = new(Options) - **out = **in + (*in).DeepCopyInto(*out) } in.Service.DeepCopyInto(&out.Service) } @@ -447,8 +702,8 @@ func (in *WMS) DeepCopyInto(out *WMS) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMS. @@ -469,6 +724,22 @@ func (in *WMS) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WMSBoundingBox) DeepCopyInto(out *WMSBoundingBox) { + *out = *in + out.BBox = in.BBox +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMSBoundingBox. +func (in *WMSBoundingBox) DeepCopy() *WMSBoundingBox { + if in == nil { + return nil + } + out := new(WMSBoundingBox) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WMSList) DeepCopyInto(out *WMSList) { *out = *in @@ -502,31 +773,93 @@ func (in *WMSList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WMSSpec) DeepCopyInto(out *WMSSpec) { +func (in *WMSService) DeepCopyInto(out *WMSService) { *out = *in + if in.Keywords != nil { + in, out := &in.Keywords, &out.Keywords + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Fees != nil { + in, out := &in.Fees, &out.Fees + *out = new(string) + **out = **in + } + if in.MaxSize != nil { + in, out := &in.MaxSize, &out.MaxSize + *out = new(int32) + **out = **in + } + if in.Inspire != nil { + in, out := &in.Inspire, &out.Inspire + *out = new(Inspire) + (*in).DeepCopyInto(*out) + } + if in.Resolution != nil { + in, out := &in.Resolution, &out.Resolution + *out = new(int32) + **out = **in + } + if in.DefResolution != nil { + in, out := &in.DefResolution, &out.DefResolution + *out = new(int32) + **out = **in + } + if in.StylingAssets != nil { + in, out := &in.StylingAssets, &out.StylingAssets + *out = new(StylingAssets) + (*in).DeepCopyInto(*out) + } + if in.Mapfile != nil { + in, out := &in.Mapfile, &out.Mapfile + *out = new(Mapfile) + (*in).DeepCopyInto(*out) + } + in.Layer.DeepCopyInto(&out.Layer) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMSSpec. -func (in *WMSSpec) DeepCopy() *WMSSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMSService. +func (in *WMSService) DeepCopy() *WMSService { if in == nil { return nil } - out := new(WMSSpec) + out := new(WMSService) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WMSStatus) DeepCopyInto(out *WMSStatus) { +func (in *WMSSpec) DeepCopyInto(out *WMSSpec) { *out = *in + if in.Lifecycle != nil { + in, out := &in.Lifecycle, &out.Lifecycle + *out = new(model.Lifecycle) + (*in).DeepCopyInto(*out) + } + if in.PodSpecPatch != nil { + in, out := &in.PodSpecPatch, &out.PodSpecPatch + *out = new(v1.PodSpec) + (*in).DeepCopyInto(*out) + } + if in.HorizontalPodAutoscalerPatch != nil { + in, out := &in.HorizontalPodAutoscalerPatch, &out.HorizontalPodAutoscalerPatch + *out = new(v2beta1.HorizontalPodAutoscalerSpec) + (*in).DeepCopyInto(*out) + } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = new(Options) + (*in).DeepCopyInto(*out) + } + in.Service.DeepCopyInto(&out.Service) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMSStatus. -func (in *WMSStatus) DeepCopy() *WMSStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WMSSpec. +func (in *WMSSpec) DeepCopy() *WMSSpec { if in == nil { return nil } - out := new(WMSStatus) + out := new(WMSSpec) in.DeepCopyInto(out) return out } diff --git a/build-push-deploy-locally.sh b/build-push-deploy-locally.sh index 977ec5b..28eaaef 100755 --- a/build-push-deploy-locally.sh +++ b/build-push-deploy-locally.sh @@ -6,12 +6,12 @@ echo "Running: make generate" make generate echo "" -echo "Running: build -t local-registry:5000/wfs-operator:$TAG --build-context repos=./.. ." -docker build -t "local-registry:5000/wfs-operator:$TAG" --build-context repos=./.. . +echo "Running: build -t local-registry:5000/wfs-wms-operator:$TAG --build-context repos=./.. ." +docker build -t "local-registry:5000/wfs-wms-operator:$TAG" --build-context repos=./.. . echo "" -echo "Running: push local-registry:5000/wfs-operator:$TAG" -docker push "local-registry:5000/wfs-operator:$TAG" +echo "Running: push local-registry:5000/wfs-wms-operator:$TAG" +docker push "local-registry:5000/wfs-wms-operator:$TAG" echo "" echo "Installing cert-manager" @@ -22,5 +22,5 @@ echo "Running: make install" make install echo "" -echo "Running: deploy IMG=local-registry:5000/wfs-operator:$TAG" -make deploy "IMG=local-registry:5000/wfs-operator:$TAG" \ No newline at end of file +echo "Running: deploy IMG=local-registry:5000/wfs-wms-operator:$TAG" +make deploy "IMG=local-registry:5000/wfs-wms-operator:$TAG" \ No newline at end of file diff --git a/config/crd/bases/pdok.nl_wfs.yaml b/config/crd/bases/pdok.nl_wfs.yaml index d79234e..8ccb9a9 100644 --- a/config/crd/bases/pdok.nl_wfs.yaml +++ b/config/crd/bases/pdok.nl_wfs.yaml @@ -847,14 +847,21 @@ spec: properties: automaticCasing: type: boolean + disableWebserviceProxy: + type: boolean includeIngress: type: boolean prefetchData: type: boolean + rewriteGroupToDataLayers: + type: boolean + validateChildStyleNameEqual: + type: boolean + validateRequests: + type: boolean required: - automaticCasing - includeIngress - - prefetchData type: object podSpecPatch: description: Optional strategic merge patch for the pod in the deployment. @@ -1015,6 +1022,20 @@ spec: - geometryType - tableName type: object + tif: + properties: + blobKey: + type: string + getFeatureInfoIncludesClass: + type: boolean + offsite: + type: string + resample: + type: string + required: + - blobKey + - offsite + type: object type: object datasetMetadataUrl: properties: diff --git a/config/crd/bases/pdok.nl_wms.yaml b/config/crd/bases/pdok.nl_wms.yaml index b0bcadb..4e58d00 100644 --- a/config/crd/bases/pdok.nl_wms.yaml +++ b/config/crd/bases/pdok.nl_wms.yaml @@ -293,9 +293,9 @@ spec: labelNoClip: type: boolean maxScale: - type: string + type: number minScale: - type: string + type: number name: type: string sourceMetadataIdentifier: @@ -306,7 +306,7 @@ spec: properties: abstract: type: string - legendfile: + legendFile: description: LegendFile is the struct containing the location of the legendfile properties: @@ -330,6 +330,7 @@ spec: visible: type: boolean required: + - labelNoClip - name - styles - visible @@ -366,7 +367,7 @@ spec: - configMapKeyRef type: object maxSize: - type: string + type: number metadataIdentifier: type: string resolution: @@ -395,8 +396,6 @@ spec: - name type: object type: array - required: - - blobKeys type: object title: type: string @@ -515,13 +514,822 @@ spec: spec: description: WMSSpec defines the desired state of WMS. properties: - foo: - description: Foo is an example field of WMS. Edit wms_types.go to - remove/update - type: string + horizontalPodAutoscalerPatch: + description: HorizontalPodAutoscalerSpec describes the desired functionality + of the HorizontalPodAutoscaler. + properties: + maxReplicas: + description: |- + maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. + It cannot be less that minReplicas. + format: int32 + type: integer + metrics: + description: |- + metrics contains the specifications for which to use to calculate the + desired replica count (the maximum replica count across all metrics will + be used). The desired replica count is calculated multiplying the + ratio between the target value and the current value by the current + number of pods. Ergo, metrics used must decrease as the pod count is + increased, and vice-versa. See the individual metric source types for + more information about how each type of metric must respond. + items: + description: |- + MetricSpec specifies how to scale based on a single metric + (only `type` and one other matching field should be set at once). + properties: + containerResource: + description: |- + container resource refers to a resource metric (such as those specified in + requests and limits) known to Kubernetes describing a single container in + each pod of the current scale target (e.g. CPU or memory). Such metrics are + built in to Kubernetes, and have special scaling options on top of those + available to normal per-pod metrics using the "pods" source. + properties: + container: + description: container is the name of the container + in the pods of the scaling target + type: string + name: + description: name is the name of the resource in question. + type: string + targetAverageUtilization: + description: |- + targetAverageUtilization is the target value of the average of the + resource metric across all relevant pods, represented as a percentage of + the requested value of the resource for the pods. + format: int32 + type: integer + targetAverageValue: + anyOf: + - type: integer + - type: string + description: |- + targetAverageValue is the target value of the average of the + resource metric across all relevant pods, as a raw value (instead of as + a percentage of the request), similar to the "pods" metric source type. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - container + - name + type: object + external: + description: |- + external refers to a global metric that is not associated + with any Kubernetes object. It allows autoscaling based on information + coming from components running outside of cluster + (for example length of queue in cloud messaging service, or + QPS from loadbalancer running outside of cluster). + properties: + metricName: + description: metricName is the name of the metric in + question. + type: string + metricSelector: + description: |- + metricSelector is used to identify a specific time series + within a given metric. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + targetAverageValue: + anyOf: + - type: integer + - type: string + description: |- + targetAverageValue is the target per-pod value of global metric (as a quantity). + Mutually exclusive with TargetValue. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + targetValue: + anyOf: + - type: integer + - type: string + description: |- + targetValue is the target value of the metric (as a quantity). + Mutually exclusive with TargetAverageValue. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - metricName + type: object + object: + description: |- + object refers to a metric describing a single kubernetes object + (for example, hits-per-second on an Ingress object). + properties: + averageValue: + anyOf: + - type: integer + - type: string + description: |- + averageValue is the target value of the average of the + metric across all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + metricName: + description: metricName is the name of the metric in + question. + type: string + selector: + description: |- + selector is the string-encoded form of a standard kubernetes label selector for the given metric + When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping + When unset, just the metricName will be used to gather metrics. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + target: + description: target is the described Kubernetes object. + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: 'Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + targetValue: + anyOf: + - type: integer + - type: string + description: targetValue is the target value of the + metric (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - metricName + - target + - targetValue + type: object + pods: + description: |- + pods refers to a metric describing each pod in the current scale target + (for example, transactions-processed-per-second). The values will be + averaged together before being compared to the target value. + properties: + metricName: + description: metricName is the name of the metric in + question + type: string + selector: + description: |- + selector is the string-encoded form of a standard kubernetes label selector for the given metric + When set, it is passed as an additional parameter to the metrics server for more specific metrics scoping + When unset, just the metricName will be used to gather metrics. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + targetAverageValue: + anyOf: + - type: integer + - type: string + description: |- + targetAverageValue is the target value of the average of the + metric across all relevant pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - metricName + - targetAverageValue + type: object + resource: + description: |- + resource refers to a resource metric (such as those specified in + requests and limits) known to Kubernetes describing each pod in the + current scale target (e.g. CPU or memory). Such metrics are built in to + Kubernetes, and have special scaling options on top of those available + to normal per-pod metrics using the "pods" source. + properties: + name: + description: name is the name of the resource in question. + type: string + targetAverageUtilization: + description: |- + targetAverageUtilization is the target value of the average of the + resource metric across all relevant pods, represented as a percentage of + the requested value of the resource for the pods. + format: int32 + type: integer + targetAverageValue: + anyOf: + - type: integer + - type: string + description: |- + targetAverageValue is the target value of the average of the + resource metric across all relevant pods, as a raw value (instead of as + a percentage of the request), similar to the "pods" metric source type. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - name + type: object + type: + description: |- + type is the type of metric source. It should be one of "ContainerResource", + "External", "Object", "Pods" or "Resource", each mapping to a matching field in the object. + type: string + required: + - type + type: object + type: array + x-kubernetes-list-type: atomic + minReplicas: + description: |- + minReplicas is the lower limit for the number of replicas to which the autoscaler + can scale down. It defaults to 1 pod. minReplicas is allowed to be 0 if the + alpha feature gate HPAScaleToZero is enabled and at least one Object or External + metric is configured. Scaling is active as long as at least one metric value is + available. + format: int32 + type: integer + scaleTargetRef: + description: |- + scaleTargetRef points to the target resource to scale, and is used to the pods for which metrics + should be collected, as well as to actually change the replica count. + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: 'Kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + required: + - maxReplicas + - scaleTargetRef + type: object + lifecycle: + properties: + ttlInDays: + format: int32 + type: integer + required: + - ttlInDays + type: object + options: + properties: + automaticCasing: + type: boolean + disableWebserviceProxy: + type: boolean + includeIngress: + type: boolean + prefetchData: + type: boolean + rewriteGroupToDataLayers: + type: boolean + validateChildStyleNameEqual: + type: boolean + validateRequests: + type: boolean + required: + - automaticCasing + - includeIngress + type: object + podSpecPatch: + description: Optional strategic merge patch for the pod in the deployment. + E.g. to patch the resources or add extra env vars. + type: object + x-kubernetes-preserve-unknown-fields: true + service: + properties: + abstract: + type: string + accessConstraints: + type: string + baseUrl: + type: string + dataEPSG: + type: string + defResolution: + format: int32 + type: integer + fees: + type: string + inspire: + properties: + language: + type: string + serviceMetadataUrl: + properties: + csw: + properties: + metadataIdentifier: + type: string + required: + - metadataIdentifier + type: object + custom: + properties: + href: + type: string + type: + type: string + required: + - href + - type + type: object + required: + - csw + type: object + spatialDatasetIdentifier: + type: string + required: + - language + - serviceMetadataUrl + - spatialDatasetIdentifier + type: object + keywords: + items: + type: string + type: array + layer: + properties: + abstract: + type: string + authority: + properties: + name: + type: string + spatialDatasetIdentifier: + type: string + url: + type: string + required: + - name + - spatialDatasetIdentifier + - url + type: object + boundingBoxes: + items: + properties: + bbox: + description: BBox defines a bounding box with coordinates + properties: + maxx: + description: Rechtsonder X coördinaat + pattern: ^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$ + type: string + maxy: + description: Rechtsonder Y coördinaat + pattern: ^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$ + type: string + minx: + description: Linksboven X coördinaat + pattern: ^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$ + type: string + miny: + description: Linksboven Y coördinaat + pattern: ^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$ + type: string + required: + - maxx + - maxy + - minx + - miny + type: object + crs: + type: string + required: + - bbox + - crs + type: object + type: array + data: + properties: + gpkg: + properties: + blobKey: + type: string + columns: + items: + properties: + alias: + type: string + name: + type: string + required: + - name + type: object + type: array + geometryType: + type: string + tableName: + type: string + required: + - blobKey + - columns + - geometryType + - tableName + type: object + postgis: + description: Postgis - reference to table in a Postgres + database + properties: + columns: + items: + properties: + alias: + type: string + name: + type: string + required: + - name + type: object + type: array + geometryType: + type: string + tableName: + type: string + required: + - columns + - geometryType + - tableName + type: object + tif: + properties: + blobKey: + type: string + getFeatureInfoIncludesClass: + type: boolean + offsite: + type: string + resample: + type: string + required: + - blobKey + - offsite + type: object + type: object + datasetMetadataUrl: + properties: + csw: + properties: + metadataIdentifier: + type: string + required: + - metadataIdentifier + type: object + custom: + properties: + href: + type: string + type: + type: string + required: + - href + - type + type: object + required: + - csw + type: object + keywords: + items: + type: string + type: array + labelNoClip: + type: boolean + layers: + description: Nested structs do not work in crd generation + x-kubernetes-preserve-unknown-fields: true + maxscaledenominator: + type: string + minscaledenominator: + type: string + name: + type: string + styles: + items: + properties: + abstract: + type: string + legend: + properties: + blobKey: + type: string + format: + type: string + height: + format: int32 + type: integer + width: + format: int32 + type: integer + required: + - blobKey + - format + - height + - width + type: object + name: + type: string + title: + type: string + visualization: + type: string + required: + - abstract + - legend + - name + - title + - visualization + type: object + type: array + title: + type: string + visible: + type: boolean + required: + - boundingBoxes + - keywords + - labelNoClip + - name + - styles + type: object + mapfile: + properties: + configMapKeyRef: + description: Selects a key from a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + required: + - configMapKeyRef + type: object + maxSize: + format: int32 + type: integer + ownerInfoRef: + type: string + resolution: + format: int32 + type: integer + stylingAssets: + properties: + blobKeys: + items: + type: string + type: array + configMapRefs: + items: + properties: + keys: + items: + type: string + type: array + name: + type: string + required: + - name + type: object + type: array + required: + - blobKeys + - configMapRefs + type: object + title: + type: string + required: + - abstract + - accessConstraints + - baseUrl + - dataEPSG + - keywords + - layer + - ownerInfoRef + - title + type: object + required: + - horizontalPodAutoscalerPatch + - lifecycle + - options + - service type: object status: - description: WMSStatus defines the observed state of WMS. + description: OperatorStatus defines the observed state of an Atom/WFS/WMS/.... + properties: + conditions: + description: |- + Each condition contains details for one aspect of the current state of this Atom. + Known .status.conditions.type are: "Reconciled" + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + operationResults: + additionalProperties: + description: OperationResult is the action result of a CreateOrUpdate + call. + type: string + description: The result of creating or updating of each derived resource + for this Atom. + type: object type: object type: object served: true diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index a27dd37..201740f 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: local-registry:5000/wfs-operator - newTag: v3.0.25 + newName: local-registry:5000/wfs-wms-operator + newTag: v3.0.17 diff --git a/go.mod b/go.mod index 2719412..1846948 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,11 @@ go 1.23.0 godebug default=go1.23 -replace github.com/pdok/smooth-operator => ../smooth-operator - require ( + github.com/cbroglie/mustache v1.4.0 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 - github.com/pdok/smooth-operator v1.0.0 + github.com/pdok/smooth-operator v0.0.2 k8s.io/api v0.32.0 k8s.io/apimachinery v0.32.0 k8s.io/client-go v0.32.0 @@ -28,7 +27,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -41,7 +40,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.22.0 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect @@ -63,30 +62,30 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.8.0 // indirect + golang.org/x/tools v0.28.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -94,7 +93,7 @@ require ( k8s.io/apiserver v0.32.0 // indirect k8s.io/component-base v0.32.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/go.sum b/go.sum index 7251637..992a1e8 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cbroglie/mustache v1.4.0 h1:Azg0dVhxTml5me+7PsZ7WPrQq1Gkf3WApcHMjMprYoU= +github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -20,14 +22,14 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -55,8 +57,8 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -97,6 +99,10 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/pdok/smooth-operator v0.0.2-0.20250311150606-315e84363ede h1:am7q+xXpjXba94mNfsRc43bGua1kiI91NlSkQyveNMA= +github.com/pdok/smooth-operator v0.0.2-0.20250311150606-315e84363ede/go.mod h1:oZWFuIKJGjN/C6ocgMNfMZ7SbLQi+N0qaWj7j95Wdec= +github.com/pdok/smooth-operator v0.0.2 h1:RWaPpP4q6bFl3UG5aYkK/GIGeqGro7bQ/tPQuqOkync= +github.com/pdok/smooth-operator v0.0.2/go.mod h1:Jz2AcpdfElRbjJsJNUuuk/o5XGZPZAC+820bVpRysbE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -126,26 +132,26 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -157,56 +163,56 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -231,8 +237,8 @@ k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=