Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd37fdc57a |
No files matched your search
@@ -7,6 +7,7 @@ to explore ArgoCD and GitOps!
|
|||||||
| Application | Description |
|
| Application | Description |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| [guestbook](guestbook/) | A hello word guestbook app as plain YAML |
|
| [guestbook](guestbook/) | A hello word guestbook app as plain YAML |
|
||||||
|
| [ksonnet-guestbook](ksonnet-guestbook/) | The guestbook app as a ksonnet app |
|
||||||
| [helm-guestbook](helm-guestbook/) | The guestbook app as a Helm chart |
|
| [helm-guestbook](helm-guestbook/) | The guestbook app as a Helm chart |
|
||||||
| [jsonnet-guestbook](jsonnet-guestbook/) | The guestbook app as a raw jsonnet |
|
| [jsonnet-guestbook](jsonnet-guestbook/) | The guestbook app as a raw jsonnet |
|
||||||
| [jsonnet-guestbook-tla](jsonnet-guestbook-tla/) | The guestbook app as a raw jsonnet with support for top level arguments |
|
| [jsonnet-guestbook-tla](jsonnet-guestbook-tla/) | The guestbook app as a raw jsonnet with support for top level arguments |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
revisionHistoryLimit: 3
|
revisionHistoryLimit: 3
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ spec:
|
|||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: {{ .Values.containerPort }}
|
containerPort: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ image:
|
|||||||
tag: 0.1
|
tag: 0.1
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
containerPort: 80
|
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 80
|
port: 80
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/lib
|
||||||
|
/.ksonnet/registries
|
||||||
|
/app.override.yaml
|
||||||
|
/.ks_environment
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: 0.1.0
|
||||||
|
environments:
|
||||||
|
default:
|
||||||
|
destination:
|
||||||
|
namespace: default
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
k8sVersion: v1.10.0
|
||||||
|
path: default
|
||||||
|
dev:
|
||||||
|
destination:
|
||||||
|
namespace: dev
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
k8sVersion: v1.10.0
|
||||||
|
path: dev
|
||||||
|
prod:
|
||||||
|
destination:
|
||||||
|
namespace: prod
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
k8sVersion: v1.10.0
|
||||||
|
path: prod
|
||||||
|
kind: ksonnet.io/app
|
||||||
|
name: guestbook
|
||||||
|
version: 0.0.1
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
local env = std.extVar("__ksonnet/environments");
|
||||||
|
local params = std.extVar("__ksonnet/params").components["guestbook-ui"];
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": {
|
||||||
|
"name": params.name
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"port": params.servicePort,
|
||||||
|
"targetPort": params.containerPort
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selector": {
|
||||||
|
"app": params.name
|
||||||
|
},
|
||||||
|
"type": params.type
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": {
|
||||||
|
"name": params.name
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"replicas": params.replicas,
|
||||||
|
"revisionHistoryLimit": 3,
|
||||||
|
"selector": {
|
||||||
|
"matchLabels": {
|
||||||
|
"app": params.name
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"app": params.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"image": params.image,
|
||||||
|
"name": params.name,
|
||||||
|
"ports": [
|
||||||
|
{
|
||||||
|
"containerPort": params.containerPort
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
global: {
|
||||||
|
// User-defined global parameters; accessible to all component and environments, Ex:
|
||||||
|
// replicas: 4,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
// Component-level parameters, defined initially from 'ks prototype use ...'
|
||||||
|
// Each object below should correspond to a component in the components/ directory
|
||||||
|
"guestbook-ui": {
|
||||||
|
containerPort: 80,
|
||||||
|
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2",
|
||||||
|
name: "ks-guestbook-ui",
|
||||||
|
replicas: 1,
|
||||||
|
servicePort: 80,
|
||||||
|
type: "LoadBalancer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
local components = std.extVar("__ksonnet/components");
|
||||||
|
components + {
|
||||||
|
// Insert user-specified overrides here.
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local base = import "base.libsonnet";
|
||||||
|
// uncomment if you reference ksonnet-lib
|
||||||
|
// local k = import "k.libsonnet";
|
||||||
|
|
||||||
|
base + {
|
||||||
|
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
|
||||||
|
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
local params = std.extVar("__ksonnet/params");
|
||||||
|
local globals = import "globals.libsonnet";
|
||||||
|
local envParams = params + {
|
||||||
|
components +: {
|
||||||
|
// Insert component parameter overrides here. Ex:
|
||||||
|
// guestbook +: {
|
||||||
|
// name: "guestbook-dev",
|
||||||
|
// replicas: params.global.replicas,
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
[x]: envParams.components[x] + globals, for x in std.objectFields(envParams.components)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local base = import "base.libsonnet";
|
||||||
|
// uncomment if you reference ksonnet-lib
|
||||||
|
// local k = import "k.libsonnet";
|
||||||
|
|
||||||
|
base + {
|
||||||
|
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
|
||||||
|
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
local params = std.extVar("__ksonnet/params");
|
||||||
|
local globals = import "globals.libsonnet";
|
||||||
|
local envParams = params + {
|
||||||
|
components +: {
|
||||||
|
// Insert component parameter overrides here. Ex:
|
||||||
|
// guestbook +: {
|
||||||
|
// name: "guestbook-dev",
|
||||||
|
// replicas: params.global.replicas,
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
[x]: envParams.components[x] + globals, for x in std.objectFields(envParams.components)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
local base = import "base.libsonnet";
|
||||||
|
// uncomment if you reference ksonnet-lib
|
||||||
|
// local k = import "k.libsonnet";
|
||||||
|
|
||||||
|
base + {
|
||||||
|
// Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
|
||||||
|
// "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
local params = std.extVar("__ksonnet/params");
|
||||||
|
local globals = import "globals.libsonnet";
|
||||||
|
local envParams = params + {
|
||||||
|
components +: {
|
||||||
|
// Insert component parameter overrides here. Ex:
|
||||||
|
// guestbook +: {
|
||||||
|
// name: "guestbook-dev",
|
||||||
|
// replicas: params.global.replicas,
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
components: {
|
||||||
|
[x]: envParams.components[x] + globals, for x in std.objectFields(envParams.components)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-2
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: nginx2
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx2
|
|
||||||
image: nginx:1.14.2
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: nginx2
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 80
|
|
||||||
selector:
|
|
||||||
app: nginx2
|
|
||||||
@@ -4,4 +4,3 @@
|
|||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| [kasane](kasane/) | The guestbook application as a `kasane` package. |
|
| [kasane](kasane/) | The guestbook application as a `kasane` package. |
|
||||||
| [kustomized-helm](kustomized-helm/) | Application comprised of a `helm` chart and customized using `kustomize` |
|
| [kustomized-helm](kustomized-helm/) | Application comprised of a `helm` chart and customized using `kustomize` |
|
||||||
| [nix](nix/) | Application comprised of a `helm` chart built and customized using `nix` |
|
|
||||||
@@ -14,14 +14,10 @@ Use following steps to try the application:
|
|||||||
command: ["/bin/sh", "-c"]
|
command: ["/bin/sh", "-c"]
|
||||||
args: ["helm dependency build"]
|
args: ["helm dependency build"]
|
||||||
generate:
|
generate:
|
||||||
command: ["/bin/sh", "-c"]
|
command: [sh, -c]
|
||||||
args: ["helm template . --name-template $ARGOCD_APP_NAME --namespace $ARGOCD_APP_NAMESPACE --kube-version $KUBE_VERSION > all.yaml && kustomize build"]
|
args: ["helm template --release-name release-name . > all.yaml && kustomize build"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
|
||||||
- `$ARGOCD_APP_NAME`, `$ARGOCD_APP_NAMESPACE` and `$KUBE_VERSION` are environment variables that exists in the context of the plugin.
|
|
||||||
- setting `--kube-version` is important as helm template can mock up data which may not match the actual cluster version.
|
|
||||||
|
|
||||||
* create application using `kustomized-helm` as a config management plugin name:
|
* create application using `kustomized-helm` as a config management plugin name:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
result
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
# nix
|
|
||||||
|
|
||||||
[nix](https://nixos.org/) is a tool that takes a unique approach to package
|
|
||||||
management and system configuration.
|
|
||||||
|
|
||||||
This setup is based on the [NixCon 2023 talk](https://www.youtube.com/watch?v=SEA1Qm8K4gY).
|
|
||||||
|
|
||||||
## Set up the argo-cd installation for nix support
|
|
||||||
|
|
||||||
This setup uses the stock `nixos/nix:latest` image without any modifications.
|
|
||||||
That requires some changes in runtime, as nix cannot run as user 999 our of the
|
|
||||||
box.
|
|
||||||
|
|
||||||
Add the following bits to the values.yaml of your helm deployment:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
repoServer:
|
|
||||||
volumes:
|
|
||||||
- name: nix-cmp-config
|
|
||||||
configMap:
|
|
||||||
name: nix-cmp-config
|
|
||||||
- name: nix-cmp-tmp
|
|
||||||
emptyDir: {}
|
|
||||||
- name: nix-cmp-nix
|
|
||||||
emptyDir: {}
|
|
||||||
- name: nix-cmp-home
|
|
||||||
emptyDir: {}
|
|
||||||
initContainers:
|
|
||||||
- name: nix-bootstrap
|
|
||||||
# the init container copies the whole nix store and profiles into the
|
|
||||||
# temporary volume and makes sure the permissions are correct
|
|
||||||
command:
|
|
||||||
- "sh"
|
|
||||||
- "-c"
|
|
||||||
- "cp -a /nix/* /nixvol && chown -R 999 /nixvol/*"
|
|
||||||
image: nixos/nix:latest
|
|
||||||
# the image will always be updated at init step, so the one in the
|
|
||||||
# extraContainers must have the policy of Never to always be the same
|
|
||||||
# exact image.
|
|
||||||
imagePullPolicy: Always
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /nixvol
|
|
||||||
name: nix-cmp-nix
|
|
||||||
extraContainers:
|
|
||||||
- name: nix-cmp-plugin
|
|
||||||
command:
|
|
||||||
- /var/run/argocd/argocd-cmp-server
|
|
||||||
image: nixos/nix:latest
|
|
||||||
imagePullPolicy: Never
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 999
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /var/run/argocd
|
|
||||||
name: var-files
|
|
||||||
- mountPath: /home/argocd/cmp-server/plugins
|
|
||||||
name: plugins
|
|
||||||
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
|
|
||||||
subPath: plugin.yaml
|
|
||||||
name: nix-cmp-config
|
|
||||||
- mountPath: /etc/passwd
|
|
||||||
subPath: passwd
|
|
||||||
name: nix-cmp-config
|
|
||||||
- mountPath: /etc/nix/nix.conf
|
|
||||||
subPath: nix.conf
|
|
||||||
name: nix-cmp-config
|
|
||||||
- mountPath: /tmp
|
|
||||||
name: nix-cmp-tmp
|
|
||||||
- mountPath: /nix
|
|
||||||
name: nix-cmp-nix
|
|
||||||
- mountPath: /home/nix
|
|
||||||
name: nix-cmp-home
|
|
||||||
```
|
|
||||||
|
|
||||||
## Add the plugin ConfigMap:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: nix-cmp-config
|
|
||||||
namespace: argocd
|
|
||||||
data:
|
|
||||||
nix.conf: |
|
|
||||||
build-users-group = nixbld
|
|
||||||
sandbox = false
|
|
||||||
experimental-features = nix-command flakes
|
|
||||||
substituters = https://cache.nixos.org https://nixhelm.cachix.org
|
|
||||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nixhelm.cachix.org-1:esqauAsR4opRF0UsGrA6H3gD21OrzMnBBYvJXeddjtY=
|
|
||||||
passwd: |
|
|
||||||
nix:x:999:30000:Nix build user 1:/home/nix:/bin/false
|
|
||||||
root:x:0:0::/root:/bin/bash
|
|
||||||
plugin.yaml: |
|
|
||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: ConfigManagementPlugin
|
|
||||||
metadata:
|
|
||||||
name: nix-cmp-plugin
|
|
||||||
spec:
|
|
||||||
discover:
|
|
||||||
fileName: flake.nix
|
|
||||||
generate:
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- "-c"
|
|
||||||
- cat result
|
|
||||||
init:
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- "-c"
|
|
||||||
- |
|
|
||||||
export OUTPUT="${ARGOCD_ENV_NIX_OUTPUT:-kubernetesConfiguration}"
|
|
||||||
echo -ne "Building for $OUTPUT\n" >/dev/stderr
|
|
||||||
if [ "$PARAM_VALUES" != "" ]; then
|
|
||||||
echo -ne "With values\n" >/dev/stderr
|
|
||||||
echo "$PARAM_VALUES" > values.json
|
|
||||||
nix-shell -p git --run ''git add values.json''
|
|
||||||
fi
|
|
||||||
if [ "$PARAM_IMPURE" == "true" ]; then
|
|
||||||
echo -ne "With impure\n" >/dev/stderr
|
|
||||||
IMPURE_FLAG="--impure"
|
|
||||||
else
|
|
||||||
IMPURE_FLAG=""
|
|
||||||
fi
|
|
||||||
nix build $IMPURE_FLAG ".#${OUTPUT}"
|
|
||||||
lockRepo: true
|
|
||||||
name: nix-cmp-plugin
|
|
||||||
version: v1.0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create a nix-based application
|
|
||||||
|
|
||||||
```
|
|
||||||
argocd app create simple-nginx \
|
|
||||||
--repo https://github.com/argoproj/argocd-example-apps \
|
|
||||||
--path plugins/nix \
|
|
||||||
--dest-server https://kubernetes.default.svc \
|
|
||||||
--dest-namespace default
|
|
||||||
```
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
inputs.nixhelm.url = "github:farcaller/nixhelm";
|
|
||||||
inputs.kubegen.url = "github:farcaller/nix-kube-generators";
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nixhelm, kubegen, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
kubelib = kubegen.lib { inherit pkgs; };
|
|
||||||
|
|
||||||
addResources = yamlObjects: resources: builtins.foldl'
|
|
||||||
(acc: y: acc ++ [ y ])
|
|
||||||
resources
|
|
||||||
yamlObjects;
|
|
||||||
|
|
||||||
# You can define k8s objects using standard nix syntax
|
|
||||||
configMap = {
|
|
||||||
apiVersion = "v1";
|
|
||||||
kind = "ConfigMap";
|
|
||||||
metadata.name = "website";
|
|
||||||
data."index.html" = ''
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Hello, nix world!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
packages.kubernetesConfiguration = pkgs.lib.pipe
|
|
||||||
{
|
|
||||||
name = "nginx";
|
|
||||||
# nixhelm provides a repository of various public helm charts converted to nix
|
|
||||||
chart = nixhelm.chartsDerivations.${system}.bitnami.nginx;
|
|
||||||
namespace = "default";
|
|
||||||
values = {
|
|
||||||
replicaCount = 2;
|
|
||||||
revisionHistoryLimit = 3;
|
|
||||||
staticSiteConfigmap = configMap.metadata.name;
|
|
||||||
};
|
|
||||||
} [
|
|
||||||
# lib.pipe is a handy function to run the processing over several functions in a sequence.
|
|
||||||
# The final output must gnerate a YAML file.
|
|
||||||
kubelib.buildHelmChart
|
|
||||||
builtins.readFile
|
|
||||||
kubelib.fromYAML
|
|
||||||
(addResources [configMap])
|
|
||||||
kubelib.mkList
|
|
||||||
kubelib.toYAMLFile
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: carts
|
|
||||||
labels:
|
|
||||||
name: carts
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
# the port that this service should serve on
|
|
||||||
- port: 80
|
|
||||||
targetPort: 80
|
|
||||||
selector:
|
|
||||||
name: carts
|
|
||||||
@@ -2,7 +2,6 @@ resources:
|
|||||||
- base/carts-db-dep.yaml
|
- base/carts-db-dep.yaml
|
||||||
- base/carts-db-svc.yaml
|
- base/carts-db-svc.yaml
|
||||||
- base/carts-dep.yaml
|
- base/carts-dep.yaml
|
||||||
- base/carts-svc.yaml
|
|
||||||
- base/catalogue-db-dep.yaml
|
- base/catalogue-db-dep.yaml
|
||||||
- base/catalogue-db-svc.yaml
|
- base/catalogue-db-svc.yaml
|
||||||
- base/catalogue-dep.yaml
|
- base/catalogue-dep.yaml
|
||||||
|
|||||||
Reference in new issue
Block a user