Skip to content

Commit 3b33217

Browse files
authored
Merge pull request #11 from PDOK/PDOK-17803-mapserver-operator-featureinfo-generator-configmap
PDOK-17803 Setup featureinfo generator in configmap
2 parents c2efbac + 7818fe8 commit 3b33217

File tree

10 files changed

+404
-24
lines changed

10 files changed

+404
-24
lines changed

api/v3/wms_types.go

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,20 @@ SOFTWARE.
2525
package v3
2626

2727
import (
28+
"maps"
29+
"slices"
30+
"sort"
31+
2832
shared_model "github.com/pdok/smooth-operator/model"
2933
autoscalingv2 "k8s.io/api/autoscaling/v2"
3034
corev1 "k8s.io/api/core/v1"
3135
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32-
"maps"
33-
"slices"
34-
"sort"
3536
)
3637

3738
const (
38-
topLayer = "topLayer"
39-
dataLayer = "dataLayer"
40-
groupLayer = "groupLayer"
39+
TopLayer = "topLayer"
40+
DataLayer = "dataLayer"
41+
GroupLayer = "groupLayer"
4142
)
4243

4344
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
@@ -161,16 +162,34 @@ func init() {
161162
SchemeBuilder.Register(&WMS{}, &WMSList{})
162163
}
163164

164-
func (layer *Layer) getAllLayers() (layers []Layer) {
165+
func (layer *Layer) GetAllLayers() (layers []Layer) {
165166
layers = append(layers, *layer)
166167
if layer.Layers != nil {
167168
for _, childLayer := range *layer.Layers {
168-
layers = append(layers, childLayer.getAllLayers()...)
169+
layers = append(layers, childLayer.GetAllLayers()...)
169170
}
170171
}
171172
return
172173
}
173174

175+
func (layer *Layer) GetParent(candidateLayer *Layer) *Layer {
176+
if candidateLayer.Layers == nil {
177+
return nil
178+
}
179+
180+
for _, childLayer := range *candidateLayer.Layers {
181+
if childLayer.Name == layer.Name {
182+
return candidateLayer
183+
} else {
184+
parent := layer.GetParent(&childLayer)
185+
if parent != nil {
186+
return parent
187+
}
188+
}
189+
}
190+
return nil
191+
}
192+
174193
func (layer *Layer) hasData() bool {
175194
switch {
176195
case layer.Data == nil:
@@ -193,17 +212,25 @@ func (layer *Layer) hasTIFData() bool {
193212
return layer.Data.TIF != nil && layer.Data.TIF.BlobKey != ""
194213
}
195214

196-
func (layer *Layer) getLayerType(service *WMSService) (layerType string) {
215+
func (layer *Layer) GetLayerType(service *WMSService) (layerType string) {
197216
switch {
198217
case layer.hasData() && layer.Layers == nil:
199-
return dataLayer
218+
return DataLayer
200219
case layer.Name == service.Layer.Name:
201-
return topLayer
220+
return TopLayer
202221
default:
203-
return groupLayer
222+
return GroupLayer
204223
}
205224
}
206225

226+
func (layer *Layer) IsDataLayer(service *WMSService) bool {
227+
return layer.GetLayerType(service) == DataLayer
228+
}
229+
230+
func (layer *Layer) IsGroupLayer(service *WMSService) bool {
231+
return layer.GetLayerType(service) == GroupLayer
232+
}
233+
207234
func (layer *Layer) hasBoundingBoxForCRS(crs string) bool {
208235
for _, bbox := range layer.BoundingBoxes {
209236
if bbox.CRS == crs {
@@ -237,7 +264,7 @@ func (layer *Layer) IsGroupLayer() bool {
237264
}
238265

239266
func (wms *WMS) GetAllLayersWithLegend() (layers []Layer) {
240-
for _, layer := range wms.Spec.Service.Layer.getAllLayers() {
267+
for _, layer := range wms.Spec.Service.Layer.GetAllLayers() {
241268
if !layer.hasData() || len(layer.Styles) == 0 {
242269
continue
243270
}
@@ -253,7 +280,7 @@ func (wms *WMS) GetAllLayersWithLegend() (layers []Layer) {
253280

254281
func (wms *WMS) GetUniqueTiffBlobKeys() []string {
255282
blobKeys := map[string]bool{}
256-
for _, layer := range wms.Spec.Service.Layer.getAllLayers() {
283+
for _, layer := range wms.Spec.Service.Layer.GetAllLayers() {
257284
if layer.hasTIFData() {
258285
blobKeys[layer.Data.TIF.BlobKey] = true
259286
}
@@ -284,7 +311,7 @@ func (wms *WMS) GetAuthority() *Authority {
284311
}
285312

286313
func (wms *WMS) HasPostgisData() bool {
287-
for _, layer := range wms.Spec.Service.Layer.getAllLayers() {
314+
for _, layer := range wms.Spec.Service.Layer.GetAllLayers() {
288315
if layer.Data != nil && layer.Data.Postgis != nil {
289316
return true
290317
}

api/v3/wms_types_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package v3
33
import (
44
"github.com/google/go-cmp/cmp"
55
"github.com/pdok/smooth-operator/model"
6+
"reflect"
67
"testing"
78
)
89

@@ -124,3 +125,39 @@ func TestLayer_setInheritedBoundingBoxes(t *testing.T) {
124125
})
125126
}
126127
}
128+
129+
func TestLayer_GetParent(t *testing.T) {
130+
childLayer2 := Layer{Name: "childlayer-2"}
131+
childLayer1 := Layer{Name: "childlayer-1", Layers: &[]Layer{childLayer2}}
132+
topLayer := Layer{Name: "toplayer", Layers: &[]Layer{childLayer1}}
133+
134+
type args struct {
135+
candidateLayer *Layer
136+
}
137+
tests := []struct {
138+
name string
139+
layer Layer
140+
args args
141+
want *Layer
142+
}{
143+
{
144+
name: "Test GetParent on layer with parent",
145+
layer: childLayer2,
146+
args: args{candidateLayer: &topLayer},
147+
want: &childLayer1,
148+
},
149+
{
150+
name: "Test GetParent on layer without parent",
151+
layer: topLayer,
152+
args: args{candidateLayer: &topLayer},
153+
want: nil,
154+
},
155+
}
156+
for _, tt := range tests {
157+
t.Run(tt.name, func(t *testing.T) {
158+
if got := tt.layer.GetParent(tt.args.candidateLayer); !reflect.DeepEqual(got, tt.want) {
159+
t.Errorf("GetParent() = %v, want %v", got, tt.want)
160+
}
161+
})
162+
}
163+
}

api/v3/wms_validation.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func validateWMS(wms *WMS, warnings *[]string, reasons *[]string) {
9393
var names []string
9494
hasVisibleLayer := false
9595
wms.Spec.Service.Layer.setInheritedBoundingBoxes()
96-
for _, layer := range wms.Spec.Service.Layer.getAllLayers() {
96+
for _, layer := range wms.Spec.Service.Layer.GetAllLayers() {
9797
var layerReasons []string
9898
if slices.Contains(names, layer.Name) {
9999
*reasons = append(*reasons, fmt.Sprintf("layer names must be unique, layer.name '%s' is duplicated", layer.Name))
@@ -162,8 +162,8 @@ func validateWMS(wms *WMS, warnings *[]string, reasons *[]string) {
162162
}
163163
}
164164
}
165-
layerType := layer.getLayerType(&service)
166-
if layerType == dataLayer {
165+
166+
if layer.IsDataLayer(&wms.Spec.Service) {
167167
for _, style := range layer.Styles {
168168
if wms.Spec.Service.Mapfile == nil && style.Visualization == nil {
169169
layerReasons = append(layerReasons, fmt.Sprintf("invalid style: '%s': style.visualization must be set on a dataLayer", style.Name))
@@ -173,7 +173,8 @@ func validateWMS(wms *WMS, warnings *[]string, reasons *[]string) {
173173
}
174174
}
175175
}
176-
if layerType == groupLayer || layerType == topLayer {
176+
layerType := layer.GetLayerType(&service)
177+
if layerType == GroupLayer || layerType == TopLayer {
177178
if !*layer.Visible {
178179
layerReasons = append(layerReasons, layerType+" must be visible")
179180
}

cmd/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const (
4949
defaultMultitoolImage = "docker.io/pdok/docker-multitool:0.9.1"
5050
defaultMapfileGeneratorImage = "docker.io/pdok/mapfile-generator:1.9.3"
5151
defaultCapabilitiesGeneratorImage = "docker.io/pdok/ogc-capabilities-generator:1.0.0-beta5"
52+
defaultFeatureinfoGeneratorImage = "docker.io/pdok/featureinfo-generator:v1.4.0-beta1"
5253
)
5354

5455
var (
@@ -78,6 +79,7 @@ func main() {
7879
var multitoolImage string
7980
var mapfileGeneratorImage string
8081
var capabilitiesGeneratorImage string
82+
var featureinfoGeneratorImage string
8183
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
8284
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
8385
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -99,6 +101,7 @@ func main() {
99101
flag.StringVar(&multitoolImage, "multitool-image", defaultMultitoolImage, "The image to use in the blob download init-container.")
100102
flag.StringVar(&mapfileGeneratorImage, "mapfile-generator-image", defaultMapfileGeneratorImage, "The image to use in the mapfile generator init-container.")
101103
flag.StringVar(&capabilitiesGeneratorImage, "capabilities-generator-image", defaultCapabilitiesGeneratorImage, "The image to use in the capabilities generator init-container.")
104+
flag.StringVar(&featureinfoGeneratorImage, "featureinfo-generator-image", defaultFeatureinfoGeneratorImage, "The image to use in the featureinfo generator init-container.")
102105

103106
opts := zap.Options{
104107
Development: true,
@@ -228,8 +231,9 @@ func main() {
228231
}
229232

230233
if err = (&controller.WMSReconciler{
231-
Client: mgr.GetClient(),
232-
Scheme: mgr.GetScheme(),
234+
Client: mgr.GetClient(),
235+
Scheme: mgr.GetScheme(),
236+
FeatureinfoGeneratorImage: featureinfoGeneratorImage,
233237
}).SetupWithManager(mgr); err != nil {
234238
setupLog.Error(err, "unable to create controller", "controller", "WMS")
235239
os.Exit(1)

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/cbroglie/mustache v1.4.0
1111
github.com/onsi/ginkgo/v2 v2.22.2
1212
github.com/onsi/gomega v1.36.2
13+
github.com/pdok/featureinfo-generator v1.4.0-beta1
1314
github.com/pdok/ogc-capabilities-generator v1.0.0-beta5
1415
github.com/pdok/ogc-specifications v1.0.0-beta5
1516
github.com/pdok/smooth-operator v0.0.9
@@ -82,7 +83,7 @@ require (
8283
github.com/google/btree v1.1.3 // indirect
8384
github.com/google/cel-go v0.22.0 // indirect
8485
github.com/google/gnostic-models v0.6.9 // indirect
85-
github.com/google/go-cmp v0.7.0 // indirect
86+
github.com/google/go-cmp v0.7.0
8687
github.com/google/gofuzz v1.2.0 // indirect
8788
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
8889
github.com/google/uuid v1.6.0 // indirect
@@ -135,7 +136,7 @@ require (
135136
k8s.io/component-base v0.32.0 // indirect
136137
k8s.io/klog/v2 v2.130.1 // indirect
137138
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
138-
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
139+
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
139140
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
140141
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
141142
sigs.k8s.io/kustomize/api v0.19.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
138138
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
139139
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
140140
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
141+
github.com/pdok/featureinfo-generator v1.4.0-beta1 h1:ZZO5OtK7yW5ozXFrl1OaxKl0MK3XF5YGFh2692JZVN8=
142+
github.com/pdok/featureinfo-generator v1.4.0-beta1/go.mod h1:02Ryu7ZRkeha8SCfS6VYWCdKYZh0llskrfFgp6xRCjk=
141143
github.com/pdok/ogc-capabilities-generator v1.0.0-beta5 h1:nwkIbFcUW4FIYnf6fLfzpkaIdolQoEhH7yyS8KXNt/g=
142144
github.com/pdok/ogc-capabilities-generator v1.0.0-beta5/go.mod h1:qG2auFy7MDL8Zp1eOfdaUJat+jNW5oLGVd+g7Mbth4Q=
143145
github.com/pdok/ogc-specifications v1.0.0-beta5 h1:j7JrXUeW55mVU9ZmJ67r8V6DhbKdgTDiGE1YKFpRWx4=
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package featureinfogenerator
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
pdoknlv3 "github.com/pdok/mapserver-operator/api/v3"
7+
corev1 "k8s.io/api/core/v1"
8+
)
9+
10+
const (
11+
htmlTemplatesPath = "/srv/data/config/templates"
12+
ConfigMapFeatureinfoGeneratorVolumeName = "featureinfo-generator-config"
13+
)
14+
15+
func GetFeatureinfoGeneratorInitContainer(image string, srvDir string) (*corev1.Container, error) {
16+
initContainer := corev1.Container{
17+
Name: "featureinfo-generator",
18+
Image: image,
19+
ImagePullPolicy: corev1.PullIfNotPresent,
20+
Command: []string{"featureinfo-generator"},
21+
Args: []string{
22+
"--input-path",
23+
"/input/input.json",
24+
"--dest-folder",
25+
htmlTemplatesPath,
26+
"--file-name",
27+
"feature-info",
28+
},
29+
VolumeMounts: []corev1.VolumeMount{
30+
{Name: "base", MountPath: srvDir + "/data", ReadOnly: false},
31+
{Name: ConfigMapFeatureinfoGeneratorVolumeName, MountPath: "/input", ReadOnly: true},
32+
},
33+
}
34+
35+
return &initContainer, nil
36+
}
37+
38+
func GetInput(wms *pdoknlv3.WMS) (string, error) {
39+
input, err := MapWMSToFeatureinfoGeneratorInput(wms)
40+
if err != nil {
41+
return "", err
42+
}
43+
jsonInput, err := json.MarshalIndent(input, "", " ")
44+
if err != nil {
45+
return "", fmt.Errorf("failed to marshal the featureinfo generator input to json: %w", err)
46+
}
47+
48+
return string(jsonInput), nil
49+
}

0 commit comments

Comments
 (0)