Skip to content

Commit f5ea086

Browse files
committed
feat: vap generation
Signed-off-by: Rita Zhang <[email protected]>
1 parent a4ffcde commit f5ea086

File tree

88 files changed

+1431
-326
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1431
-326
lines changed

.github/workflows/workflow.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0
5151
with:
5252
# version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
53-
version: v1.54.2
53+
version: v1.55.2
5454

5555
test:
5656
name: "Unit test"
@@ -178,7 +178,7 @@ jobs:
178178
IMG=gatekeeper-e2e:latest \
179179
USE_LOCAL_IMG=true
180180
181-
make test-e2e
181+
make test-e2e KUBERNETES_VERSION=${{ matrix.KUBERNETES_VERSION }} ENABLE_VAP_TESTS=1
182182
183183
- name: Save logs
184184
if: ${{ always() }}
@@ -219,6 +219,7 @@ jobs:
219219
make e2e-bootstrap
220220
221221
- name: Run e2e
222+
# TODO(ritazh): add helm chart values for vap feature before alpha release
222223
run: |
223224
make docker-buildx \
224225
IMG=gatekeeper-e2e:latest \
@@ -288,7 +289,7 @@ jobs:
288289
make e2e-bootstrap
289290
290291
- name: Run e2e
291-
run: |
292+
run: | # TODO(ritazh): set ENABLE_VAP_TESTS=1 before alpha release
292293
make docker-buildx \
293294
IMG=gatekeeper-e2e:latest
294295

Makefile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ KUSTOMIZE_VERSION ?= 3.8.9
2323
BATS_VERSION ?= 1.8.2
2424
ORAS_VERSION ?= 0.16.0
2525
BATS_TESTS_FILE ?= test/bats/test.bats
26+
KIND_CLUSTER_FILE ?= test/bats/tests/kindcluster.yml
2627
HELM_VERSION ?= 3.7.2
2728
NODE_VERSION ?= 16-bullseye-slim
2829
YQ_VERSION ?= 4.30.6
@@ -32,7 +33,7 @@ GATEKEEPER_NAMESPACE ?= gatekeeper-system
3233

3334
# When updating this, make sure to update the corresponding action in
3435
# workflow.yaml
35-
GOLANGCI_LINT_VERSION := v1.51.2
36+
GOLANGCI_LINT_VERSION := v1.55.2
3637

3738
# Detects the location of the user golangci-lint cache.
3839
GOLANGCI_LINT_CACHE := $(shell pwd)/.tmp/golangci-lint
@@ -70,6 +71,8 @@ MANAGER_IMAGE_PATCH := "apiVersion: apps/v1\
7071
\n - --disable-opa-builtin=http.send\
7172
\n - --log-mutations\
7273
\n - --mutation-annotations\
74+
\n - --vap-enforcement=GATEKEEPER_DEFAULT\
75+
\n - --experimental-enable-k8s-native-validation\
7376
\n---\
7477
\napiVersion: apps/v1\
7578
\nkind: Deployment\
@@ -89,7 +92,10 @@ MANAGER_IMAGE_PATCH := "apiVersion: apps/v1\
8992
\n - --operation=status\
9093
\n - --operation=mutation-status\
9194
\n - --audit-chunk-size=500\
92-
\n - --logtostderr"
95+
\n - --logtostderr\
96+
\n - --vap-enforcement=GATEKEEPER_DEFAULT\
97+
\n - --experimental-enable-k8s-native-validation\
98+
\n"
9399

94100
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
95101
ifeq (,$(shell go env GOBIN))
@@ -160,8 +166,10 @@ KIND_NODE_VERSION := kindest/node:v$(KUBERNETES_VERSION)
160166
e2e-bootstrap: e2e-dependencies
161167
# Check for existing kind cluster
162168
if [ $$(${GITHUB_WORKSPACE}/bin/kind get clusters) ]; then ${GITHUB_WORKSPACE}/bin/kind delete cluster; fi
169+
163170
# Create a new kind cluster
164-
TERM=dumb ${GITHUB_WORKSPACE}/bin/kind create cluster --image $(KIND_NODE_VERSION) --wait 5m
171+
# TODO(ritazh): remove KIND_CLUSTER_FILE when vap feature is GA
172+
if [ $$(echo $(KUBERNETES_VERSION) | cut -d'.' -f2) -lt 28 ]; then ${GITHUB_WORKSPACE}/bin/kind create cluster --image $(KIND_NODE_VERSION) --wait 5m; else ${GITHUB_WORKSPACE}/bin/kind create cluster --config $(KIND_CLUSTER_FILE) --image $(KIND_NODE_VERSION) --wait 5m; fi
165173

166174
e2e-build-load-image: docker-buildx e2e-build-load-externaldata-image
167175
kind load docker-image --name kind ${IMG} ${CRD_IMG}

apis/status/v1beta1/constraintpodstatus_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type ConstraintPodStatusStatus struct {
4646
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
4747
}
4848

49-
// Error represents a single error caught while adding a constraint to OPA.
49+
// Error represents a single error caught while adding a constraint to engine.
5050
type Error struct {
5151
Code string `json:"code"`
5252
Message string `json:"message"`

cmd/build/helmify/main.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ var kindRegex = regexp.MustCompile(`(?m)^kind:[\s]+([\S]+)[\s]*$`)
2121
// use exactly two spaces to be sure we are capturing metadata.name.
2222
var nameRegex = regexp.MustCompile(`(?m)^ name:[\s]+([\S]+)[\s]*$`)
2323

24-
const DeploymentKind = "Deployment"
24+
const (
25+
DeploymentKind = "Deployment"
26+
end = "{{- end }}"
27+
)
2528

2629
func isRbacKind(str string) bool {
2730
rbacKinds := [4]string{"Role", "ClusterRole", "RoleBinding", "ClusterRoleBinding"}
@@ -105,12 +108,12 @@ func (ks *kindSet) Write() error {
105108
fileName := fmt.Sprintf("%s-%s.yaml", strings.ToLower(name), strings.ToLower(kind))
106109

107110
if name == "validation.gatekeeper.sh" {
108-
obj = "{{- if not .Values.disableValidatingWebhook }}\n" + obj + "{{- end }}\n"
111+
obj = "{{- if not .Values.disableValidatingWebhook }}\n" + obj + end + "\n"
109112
fileName = fmt.Sprintf("gatekeeper-validating-webhook-configuration-%s.yaml", strings.ToLower(kind))
110113
}
111114

112115
if name == "mutation.gatekeeper.sh" {
113-
obj = "{{- if not .Values.disableMutation }}\n" + obj + "{{- end }}\n"
116+
obj = "{{- if not .Values.disableMutation }}\n" + obj + end + "\n"
114117
fileName = fmt.Sprintf("gatekeeper-mutating-webhook-configuration-%s.yaml", strings.ToLower(kind))
115118
}
116119

@@ -121,7 +124,7 @@ func (ks *kindSet) Write() error {
121124
}
122125

123126
if name == "gatekeeper-critical-pods" && kind == "ResourceQuota" {
124-
obj = "{{- if .Values.resourceQuota }}\n" + obj + "{{- end }}\n"
127+
obj = "{{- if .Values.resourceQuota }}\n" + obj + end + "\n"
125128
}
126129

127130
if name == "gatekeeper-controller-manager" && kind == DeploymentKind {
@@ -143,7 +146,7 @@ func (ks *kindSet) Write() error {
143146
}
144147

145148
if isRbacKind(kind) {
146-
obj = "{{- if .Values.rbac.create }}\n" + obj + "{{- end }}\n"
149+
obj = "{{- if .Values.rbac.create }}\n" + obj + end + "\n"
147150
}
148151

149152
if name == "gatekeeper-controller-manager" && kind == "PodDisruptionBudget" {

config/crd/bases/status.gatekeeper.sh_constraintpodstatuses.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ spec:
4646
errors:
4747
items:
4848
description: Error represents a single error caught while adding
49-
a constraint to OPA.
49+
a constraint to engine.
5050
properties:
5151
code:
5252
type: string

config/rbac/role.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ rules:
3232
- patch
3333
- update
3434
- watch
35+
- apiGroups:
36+
- admissionregistration.k8s.io
37+
resources:
38+
- validatingadmissionpolicies
39+
- validatingadmissionpolicybindings
40+
verbs:
41+
- create
42+
- delete
43+
- get
44+
- list
45+
- patch
46+
- update
47+
- watch
3548
- apiGroups:
3649
- apiextensions.k8s.io
3750
resources:

demo/k8s-validating-admission-policy/k8srequiredlabels_template.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ apiVersion: templates.gatekeeper.sh/v1
22
kind: ConstraintTemplate
33
metadata:
44
name: k8srequiredlabels
5+
labels:
6+
"gatekeeper.sh/use-vap": "yes"
57
spec:
68
crd:
79
spec:
@@ -29,7 +31,7 @@ spec:
2931
- engine: K8sNativeValidation
3032
source:
3133
validations:
32-
- expression: "variables.params.labels.all(entry, has(object.metadata.labels) && entry.key in object.metadata.labels)"
34+
- expression: '[object, oldObject].exists(obj, obj != null && has(obj.metadata) && variables.params.labels.all(entry, has(obj.metadata.labels) && entry.key in obj.metadata.labels))'
3335
messageExpression: '"missing required label, requires all of: " + variables.params.labels.map(entry, entry.key).join(", ")'
34-
- expression: "!variables.params.labels.exists(entry, has(object.metadata.labels) && entry.key in object.metadata.labels && !string(object.metadata.labels[entry.key]).matches(string(entry.allowedRegex)))"
36+
- expression: '[object, oldObject].exists(obj, obj != null && !variables.params.labels.exists(entry, has(obj.metadata.labels) && entry.key in obj.metadata.labels && !string(obj.metadata.labels[entry.key]).matches(string(entry.allowedRegex))))'
3537
message: "regex mismatch"

demo/k8s-validating-admission-policy/owner_must_be_provided.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ apiVersion: constraints.gatekeeper.sh/v1beta1
22
kind: K8sRequiredLabels
33
metadata:
44
name: all-must-have-owner
5+
labels:
6+
"gatekeeper.sh/use-vap": "yes"
57
spec:
68
match:
79
kinds:

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ require (
1313
github.com/golang/protobuf v1.5.3
1414
github.com/google/go-cmp v0.6.0
1515
github.com/google/uuid v1.5.0
16-
github.com/onsi/gomega v1.30.0
16+
github.com/onsi/gomega v1.31.1
1717
github.com/open-policy-agent/cert-controller v0.10.1
18-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240110234408-18fa1fc7dc06
18+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240219192228-76869f816908
1919
github.com/pkg/errors v0.9.1
2020
github.com/prometheus/client_golang v1.18.0
2121
github.com/spf13/cobra v1.8.0
@@ -30,7 +30,7 @@ require (
3030
go.opentelemetry.io/otel/sdk/metric v1.19.0
3131
go.uber.org/automaxprocs v1.5.3
3232
go.uber.org/zap v1.26.0
33-
golang.org/x/net v0.20.0
33+
golang.org/x/net v0.21.0
3434
golang.org/x/oauth2 v0.16.0
3535
golang.org/x/sync v0.6.0
3636
golang.org/x/time v0.5.0
@@ -138,10 +138,10 @@ require (
138138
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
139139
go.uber.org/atomic v1.11.0 // indirect
140140
go.uber.org/multierr v1.11.0 // indirect
141-
golang.org/x/crypto v0.18.0 // indirect
141+
golang.org/x/crypto v0.19.0 // indirect
142142
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
143-
golang.org/x/sys v0.16.0 // indirect
144-
golang.org/x/term v0.16.0 // indirect
143+
golang.org/x/sys v0.17.0 // indirect
144+
golang.org/x/term v0.17.0 // indirect
145145
golang.org/x/text v0.14.0 // indirect
146146
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
147147
google.golang.org/api v0.160.0 // indirect

go.sum

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,14 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
286286
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
287287
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
288288
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
289-
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
290-
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
291-
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
292-
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
289+
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
290+
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
291+
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
292+
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
293293
github.com/open-policy-agent/cert-controller v0.10.1 h1:RXSYoyn8FdCenWecRP//UV5nbVfmstNpj4kHQFkvPK4=
294294
github.com/open-policy-agent/cert-controller v0.10.1/go.mod h1:4uRbBLY5DsPOog+a9pqk3JLxuuhrWsbUedQW65HcLTI=
295-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240110234408-18fa1fc7dc06 h1:scXMWxph905CdmX5HkFJXipCtG+wT1ynxw31G9qSrMk=
296-
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240110234408-18fa1fc7dc06/go.mod h1:Gl2I/z5dxvTOwa/ANYGGOkUqE4M0CbQpln0Ia/7KVro=
295+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240219192228-76869f816908 h1:VwTOJNE/PuNXjxsgG85d/lVrwYSFYQFDt95KEzGNS0M=
296+
github.com/open-policy-agent/frameworks/constraint v0.0.0-20240219192228-76869f816908/go.mod h1:MoEB2MwxsQL+xUDG6WdIpJ6gU+gEQuiBre67F3C+p3I=
297297
github.com/open-policy-agent/opa v0.60.0 h1:ZPoPt4yeNs5UXCpd/P/btpSyR8CR0wfhVoh9BOwgJNs=
298298
github.com/open-policy-agent/opa v0.60.0/go.mod h1:aD5IK6AiLNYBjNXn7E02++yC8l4Z+bRDvgM6Ss0bBzA=
299299
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -426,8 +426,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
426426
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
427427
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
428428
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
429-
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
430-
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
429+
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
430+
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
431431
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
432432
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
433433
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -455,8 +455,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
455455
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
456456
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
457457
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
458-
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
459-
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
458+
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
459+
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
460460
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
461461
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
462462
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
@@ -486,12 +486,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
486486
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
487487
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
488488
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
489-
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
490-
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
489+
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
490+
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
491491
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
492492
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
493-
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
494-
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
493+
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
494+
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
495495
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
496496
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
497497
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

0 commit comments

Comments
 (0)