fix
This commit is contained in:
661
share/gitea-tekton-org/auto_ConfigMap.tf
Normal file
661
share/gitea-tekton-org/auto_ConfigMap.tf
Normal file
@@ -0,0 +1,661 @@
|
||||
resource "kubectl_manifest" "ConfigMap_auto-cd-templates" {
|
||||
yaml_body = <<-EOF
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: auto-cd-templates
|
||||
namespace: ${var.namespace}
|
||||
ownerReferences: ${jsonencode(var.install_owner)}
|
||||
labels: ${jsonencode(local.common_labels)}
|
||||
data:
|
||||
functions.sh: ${jsonencode(file("${path.module}/functions.sh"))}
|
||||
ci-kusto.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: $${PROJECT_NAME}-
|
||||
resources:
|
||||
- ../../bases/trigger-push
|
||||
- ../../bases/trigger-tag
|
||||
patches:
|
||||
- target:
|
||||
kind: Trigger
|
||||
name: push
|
||||
patch: |-
|
||||
- op: replace
|
||||
path: /spec/template/spec/resourcetemplates/0/spec/pipelineRef/name
|
||||
value: auto-ci-push
|
||||
- target:
|
||||
kind: Trigger
|
||||
name: push
|
||||
patch: |-
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: Trigger
|
||||
metadata:
|
||||
name: "push"
|
||||
spec:
|
||||
interceptors:
|
||||
- name: project-name
|
||||
ref:
|
||||
kind: ClusterInterceptor
|
||||
name: cel
|
||||
params:
|
||||
- name: filter
|
||||
value: body.repository.name == '$${PROJECT_NAME}'
|
||||
- target:
|
||||
kind: Trigger
|
||||
name: tag
|
||||
patch: |-
|
||||
- op: replace
|
||||
path: /spec/template/spec/resourcetemplates/0/spec/pipelineRef/name
|
||||
value: auto-ci-tag
|
||||
- target:
|
||||
kind: Trigger
|
||||
name: tag
|
||||
patch: |-
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: Trigger
|
||||
metadata:
|
||||
name: "tag"
|
||||
spec:
|
||||
interceptors:
|
||||
- name: project-name
|
||||
ref:
|
||||
kind: ClusterInterceptor
|
||||
name: cel
|
||||
params:
|
||||
- name: filter
|
||||
value: body.repository.name == '$${PROJECT_NAME}'
|
||||
deploy-project-kusto.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: $${PROJECT_NAME}-
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: $${ORG_NAME}
|
||||
app.kubernetes.io/component: $${PROJECT_NAME}
|
||||
component: $${PROJECT_NAME}
|
||||
resources:
|
||||
- ../../../../bases/install
|
||||
- ../../../../bases/images
|
||||
- policy.yaml
|
||||
patches:
|
||||
- target:
|
||||
kind: ImageRepository
|
||||
name: repo
|
||||
patch: |-
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImageRepository
|
||||
metadata:
|
||||
name: repo
|
||||
spec:
|
||||
image: $${ARTIFACTORY_URL}/$${PROJECT_PATH}
|
||||
- target:
|
||||
kind: Kustomization
|
||||
name: install
|
||||
patch: |-
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: install
|
||||
spec:
|
||||
path: "./stages/$${STAGE}/$${PROJECT_NAME}"
|
||||
targetNamespace: "$${DOMAIN}-org-$${ORG_NAME}-$${STAGE}"
|
||||
- target:
|
||||
kind: ImageUpdateAutomation
|
||||
name: update
|
||||
patch: |-
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta1
|
||||
kind: ImageUpdateAutomation
|
||||
metadata:
|
||||
name: update
|
||||
spec:
|
||||
update:
|
||||
path: ./stages/$${STAGE}/$${PROJECT_NAME}
|
||||
deploy-policy-tag.yaml.tmpl: |-
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImagePolicy
|
||||
metadata:
|
||||
name: policy
|
||||
labels:
|
||||
app.kubernetes.io/component: $${PROJECT_NAME}
|
||||
app.kubernetes.io/name: $${ORG_NAME}
|
||||
component: $${PROJECT_NAME}
|
||||
spec:
|
||||
imageRepositoryRef:
|
||||
name: $${PROJECT_NAME}-repo
|
||||
filterTags:
|
||||
pattern: '^v(?P<semver>.*)$'
|
||||
extract: '$semver'
|
||||
policy:
|
||||
semver:
|
||||
range: '>=0.1.0'
|
||||
deploy-policy-default.yaml.tmpl: |-
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImagePolicy
|
||||
metadata:
|
||||
name: policy
|
||||
labels:
|
||||
app.kubernetes.io/component: $${PROJECT_NAME}
|
||||
app.kubernetes.io/name: $${ORG_NAME}
|
||||
component: $${PROJECT_NAME}
|
||||
spec:
|
||||
imageRepositoryRef:
|
||||
name: $${PROJECT_NAME}-repo
|
||||
filterTags:
|
||||
pattern: '^main-(?P<semver>.*)$'
|
||||
extract: '$semver'
|
||||
policy:
|
||||
semver:
|
||||
range: '>=0.1.0'
|
||||
README.md: |-
|
||||
# Deployment configuration
|
||||
Only valid if FluxCD is activated in the cluster
|
||||
## File structure
|
||||
- `ci/<project_name>`: Configuration for Tekton pipeline for <project_name>. Should contain triggers and pipelines.
|
||||
- `projects/<project_name>`: Global configuration for <project_name> deployment, affect every stages
|
||||
- `stages/<stage>/<project_name>`: Configuration for <project_name> deployment in the <stage> plateform
|
||||
- `stages/<stage>/deploy`: FluxCD configuration for <stage>, should be deployed on that stage namespace as it deploy everything needed to deploy the projects
|
||||
trigger-kusto.yaml: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- trigger.yaml
|
||||
trigger-push.yaml: |-
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: Trigger
|
||||
metadata:
|
||||
name: "push"
|
||||
labels:
|
||||
type: branch-push
|
||||
spec:
|
||||
bindings:
|
||||
- name: artifactory-url
|
||||
value: "$(extensions.artifactory-url)"
|
||||
- name: project-name
|
||||
value: "$(extensions.project-name)"
|
||||
- name: project-path
|
||||
value: "$(extensions.project-path)"
|
||||
- name: git-repository-url
|
||||
value: "$(extensions.git-repository-url)"
|
||||
- name: git-revision
|
||||
value: "$(extensions.git-revision)"
|
||||
- name: branch-name
|
||||
value: "$(extensions.branch-name)"
|
||||
- name: git-default-branch
|
||||
value: "$(extensions.git-default-branch)"
|
||||
- name: generate-name
|
||||
value: "$(extensions.generate-name)"
|
||||
template:
|
||||
spec:
|
||||
params:
|
||||
- name: artifactory-url
|
||||
description: The url of the current artifactory
|
||||
- name: project-name
|
||||
description: The git repository name
|
||||
- name: project-path
|
||||
description: The path of the current project
|
||||
- name: git-repository-url
|
||||
description: The git repository url
|
||||
- name: git-revision
|
||||
description: The git revision
|
||||
default: main
|
||||
- name: git-default-branch
|
||||
description: The git revision
|
||||
default: main
|
||||
- name: branch-name
|
||||
description: The git branch
|
||||
default: main
|
||||
- name: generate-name
|
||||
resourcetemplates:
|
||||
- apiVersion: tekton.dev/v1beta1
|
||||
kind: PipelineRun
|
||||
metadata:
|
||||
generateName: $(tt.params.generate-name)-
|
||||
annotations:
|
||||
"mayfly.cloud.namecheap.com/expire": "336h" # 2 weeks
|
||||
spec:
|
||||
pipelineRef:
|
||||
name: "auto-ci-push"
|
||||
params:
|
||||
- name: artifactory-url
|
||||
value: $(tt.params.artifactory-url)
|
||||
- name: project-name
|
||||
value: $(tt.params.project-name)
|
||||
- name: project-path
|
||||
value: $(tt.params.project-path)
|
||||
- name: git-url
|
||||
value: $(tt.params.git-repository-url)
|
||||
- name: git-revision
|
||||
value: $(tt.params.git-revision)
|
||||
- name: git-default-branch
|
||||
value: $(tt.params.git-default-branch)
|
||||
- name: branch-name
|
||||
value: $(tt.params.branch-name)
|
||||
workspaces:
|
||||
- name: source
|
||||
persistentVolumeClaim:
|
||||
claimName: source
|
||||
subPath: $(tt.params.git-revision)
|
||||
- name: dockerconfig
|
||||
secret:
|
||||
secretName: gitea-docker
|
||||
items:
|
||||
- key: ".dockerconfigjson"
|
||||
path: "config.json"
|
||||
- name: sslcertdir
|
||||
secret:
|
||||
secretName: gitea
|
||||
items:
|
||||
- key: "ca.crt"
|
||||
path: "ca.crt"
|
||||
- name: ssh
|
||||
secret:
|
||||
secretName: ssh-credentials
|
||||
items:
|
||||
- key: "known_hosts"
|
||||
path: "known_hosts"
|
||||
- key: "ssh-privatekey"
|
||||
path: "id_rsa"
|
||||
- key: "ssh-publickey"
|
||||
path: "id_rsa.pub"
|
||||
trigger-tag.yaml: |-
|
||||
apiVersion: triggers.tekton.dev/v1beta1
|
||||
kind: Trigger
|
||||
metadata:
|
||||
name: "tag"
|
||||
labels:
|
||||
type: tag-push
|
||||
spec:
|
||||
bindings:
|
||||
- name: artifactory-url
|
||||
value: "$(extensions.artifactory-url)"
|
||||
- name: project-name
|
||||
value: "$(extensions.project-name)"
|
||||
- name: project-path
|
||||
value: "$(extensions.project-path)"
|
||||
- name: git-repository-url
|
||||
value: "$(extensions.git-repository-url)"
|
||||
- name: git-revision
|
||||
value: "$(extensions.git-revision)"
|
||||
- name: tag-name
|
||||
value: $(extensions.tag-name)
|
||||
- name: generate-name
|
||||
value: "$(extensions.generate-name)"
|
||||
template:
|
||||
spec:
|
||||
params:
|
||||
- name: artifactory-url
|
||||
description: The url of the current artifactory
|
||||
- name: project-name
|
||||
description: The git repository name
|
||||
- name: project-path
|
||||
description: The path of the current project
|
||||
- name: git-repository-url
|
||||
description: The git repository url
|
||||
- name: git-revision
|
||||
description: The git revision
|
||||
default: main
|
||||
- name: tag-name
|
||||
description: The git tag
|
||||
- name: generate-name
|
||||
resourcetemplates:
|
||||
- apiVersion: tekton.dev/v1beta1
|
||||
kind: PipelineRun
|
||||
metadata:
|
||||
generateName: $(tt.params.generate-name)-
|
||||
annotations:
|
||||
"mayfly.cloud.namecheap.com/expire": "1440h" # 2 months
|
||||
spec:
|
||||
pipelineRef:
|
||||
name: "auto-ci-tag"
|
||||
params:
|
||||
- name: artifactory-url
|
||||
value: $(tt.params.artifactory-url)
|
||||
- name: project-name
|
||||
value: $(tt.params.project-name)
|
||||
- name: project-path
|
||||
value: $(tt.params.project-path)
|
||||
- name: git-url
|
||||
value: $(tt.params.git-repository-url)
|
||||
- name: git-revision
|
||||
value: $(tt.params.git-revision)
|
||||
- name: tag-name
|
||||
value: $(tt.params.tag-name)
|
||||
workspaces:
|
||||
- name: source
|
||||
persistentVolumeClaim:
|
||||
claimName: source
|
||||
subPath: $(tt.params.git-revision)
|
||||
- name: dockerconfig
|
||||
secret:
|
||||
secretName: gitea-docker
|
||||
items:
|
||||
- key: ".dockerconfigjson"
|
||||
path: "config.json"
|
||||
- name: sslcertdir
|
||||
secret:
|
||||
secretName: gitea
|
||||
items:
|
||||
- key: "ca.crt"
|
||||
path: "ca.crt"
|
||||
- name: ssh
|
||||
secret:
|
||||
secretName: ssh-credentials
|
||||
items:
|
||||
- key: "known_hosts"
|
||||
path: "known_hosts"
|
||||
- key: "ssh-privatekey"
|
||||
path: "id_rsa"
|
||||
- key: "ssh-publickey"
|
||||
path: "id_rsa.pub"
|
||||
empty-kusto.yaml: |-
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
images-kusto.yaml: |-
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- repo.yaml
|
||||
- update.yaml
|
||||
stage-kusto.yaml.tmpl: |-
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../../projects/$${PROJECT_NAME}
|
||||
- ingress.yaml
|
||||
commonLabels:
|
||||
app.kubernetes.io/instance: $${STAGE}
|
||||
images:
|
||||
- name: appli
|
||||
newName: $${ARTIFACTORY_URL}/$${ORG_NAME}/$${PROJECT_NAME}
|
||||
newTag: latest # {"$imagepolicy": "$${DOMAIN}-org-$${ORG_NAME}-$${STAGE}:$${PROJECT_NAME}-policy:tag"}
|
||||
patches:
|
||||
- target:
|
||||
kind: ConfigMap
|
||||
name: $${PROJECT_NAME}-config
|
||||
path: config.yaml
|
||||
- target:
|
||||
kind: Certificate
|
||||
name: $${PROJECT_NAME}-web
|
||||
path: cert.yaml
|
||||
stage-ingress.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: $${PROJECT_NAME}-web
|
||||
labels:
|
||||
app.kubernetes.io/component: $${PROJECT_NAME}
|
||||
app.kubernetes.io/name: $${ORG_NAME}
|
||||
component: $${PROJECT_NAME}
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- $${PROJECT_NAME}.$${STAGE}.$${ORG_NAME}.$${DOMAIN_NAME}
|
||||
secretName: cert
|
||||
rules:
|
||||
- host: $${PROJECT_NAME}.$${STAGE}.$${ORG_NAME}.$${DOMAIN_NAME}
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: svc
|
||||
port:
|
||||
number: 80
|
||||
path: /
|
||||
pathType: Prefix
|
||||
stage-cert.yaml.tmpl: |-
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: $${PROJECT_NAME}-web
|
||||
spec:
|
||||
secretName: $${PROJECT_NAME}-cert
|
||||
dnsNames:
|
||||
- $${PROJECT_NAME}.$${STAGE}.$${ORG_NAME}.$${DOMAIN_NAME}
|
||||
stage-config.yaml.tmpl: |-
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: $${PROJECT_NAME}-config
|
||||
data:
|
||||
ENV_VAR_NAME: env_var_value
|
||||
yamllint.yaml: |-
|
||||
---
|
||||
extends: default
|
||||
rules:
|
||||
document-start:
|
||||
present: true
|
||||
empty-lines:
|
||||
max-end: 1
|
||||
indentation:
|
||||
spaces: 2
|
||||
indent-sequences: false
|
||||
line-length: disable
|
||||
colons:
|
||||
max-spaces-after: -1
|
||||
base-kusto.yaml: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- cert.yaml
|
||||
- deploy.yaml
|
||||
- service.yaml
|
||||
- config.yaml
|
||||
- secret.yaml
|
||||
base-update.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta1
|
||||
kind: ImageUpdateAutomation
|
||||
metadata:
|
||||
name: update
|
||||
spec:
|
||||
interval: 5m
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: deploy-git
|
||||
git:
|
||||
checkout:
|
||||
ref:
|
||||
branch: main
|
||||
commit:
|
||||
author:
|
||||
email: fluxcd.automation@$${ARTIFACTORY_URL}
|
||||
name: fluxcd
|
||||
messageTemplate: |
|
||||
Automated image update: {{ .AutomationObject }}
|
||||
|
||||
Files:
|
||||
{{ range $filename, $_ := .Updated.Files -}}
|
||||
- {{ $filename }}
|
||||
{{ end -}}
|
||||
|
||||
Objects:
|
||||
{{ range $resource, $_ := .Updated.Objects -}}
|
||||
- {{ $resource.Kind }} {{ $resource.Name }}
|
||||
{{ end -}}
|
||||
|
||||
Images:
|
||||
{{ range .Updated.Images -}}
|
||||
- {{.}}
|
||||
{{ end -}}
|
||||
{{- $ChangeId := .AutomationObject -}}
|
||||
{{- $ChangeId = printf "%s-%s" $ChangeId ( .Updated.Files | toString ) -}}
|
||||
{{- $ChangeId = printf "%s-%s" $ChangeId ( .Updated.Objects | toString ) -}}
|
||||
{{- $ChangeId = printf "%s-%s" $ChangeId ( .Updated.Images | toString ) }}
|
||||
Change-Name: {{ $ChangeId }}
|
||||
Change-Id: {{ printf "I%s" ( sha256sum $ChangeId | trunc 40 ) }}
|
||||
push:
|
||||
branch: main
|
||||
update:
|
||||
strategy: Setters
|
||||
base-repo.yaml: |-
|
||||
---
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImageRepository
|
||||
metadata:
|
||||
name: repo
|
||||
spec:
|
||||
interval: 5m
|
||||
provider: generic
|
||||
secretRef:
|
||||
name: gitea-docker
|
||||
certSecretRef:
|
||||
name: ssh-credentials-flux
|
||||
base-cert.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: web
|
||||
spec:
|
||||
issuerRef:
|
||||
group: cert-manager.io
|
||||
kind: ClusterIssuer
|
||||
name: $${ISSUER_NAME}
|
||||
base-deploy.yaml: |-
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: app
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
template:
|
||||
spec:
|
||||
securityContext:
|
||||
runAsGroup: 1000
|
||||
runAsUser: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: app
|
||||
image: appli
|
||||
imagePullPolicy: IfNotPresent
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: "config"
|
||||
- secretRef:
|
||||
name: "secret"
|
||||
base-secret.yaml: |-
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "secret"
|
||||
annotations:
|
||||
gramo.solidite.fr/no-parent: "true"
|
||||
labels:
|
||||
k8up.io/backup: "true"
|
||||
type: Opaque
|
||||
base-config.yaml: |-
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "config"
|
||||
labels:
|
||||
k8up.io/backup: "true"
|
||||
data:
|
||||
base-service.yaml: |-
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: svc
|
||||
spec:
|
||||
ports:
|
||||
- name: app
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: app
|
||||
type: ClusterIP
|
||||
install-install.yaml: |-
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: install
|
||||
spec:
|
||||
interval: 5m
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: deploy-git
|
||||
prune: true
|
||||
timeout: 1m
|
||||
install-kusto.yaml: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- install.yaml
|
||||
deploy-repo.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: git
|
||||
spec:
|
||||
interval: 5m0s
|
||||
url: $${DEPLOY_URL}
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
name: ssh-credentials
|
||||
deploy-kusto.yaml: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: deploy-
|
||||
resources:
|
||||
- repo.yaml
|
||||
- ../install
|
||||
project-kusto.yaml.tmpl: |-
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namePrefix: $${PROJECT_NAME}-
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: $${ORG_NAME}
|
||||
app.kubernetes.io/component: $${PROJECT_NAME}
|
||||
component: $${PROJECT_NAME}
|
||||
|
||||
resources:
|
||||
- ../../bases/project
|
||||
|
||||
patches:
|
||||
- target:
|
||||
kind: Deployment
|
||||
name: app
|
||||
patch: |-
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: app
|
||||
annotations:
|
||||
configmap.reloader.stakater.com/reload: "$${PROJECT_NAME}-config"
|
||||
secret.reloader.stakater.com/reload: "$${PROJECT_NAME}-secret"
|
||||
spec:
|
||||
selector:
|
||||
template:
|
||||
spec:
|
||||
securityContext:
|
||||
runAsGroup: 1000
|
||||
runAsUser: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: app
|
||||
ports:
|
||||
- name: app
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
EOF
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user