diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..03d39c0 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(docker stop:*)", + "Bash(docker rm:*)" + ] + } +} diff --git a/.gitignore b/.gitignore index 1841901..955cdc1 100644 --- a/.gitignore +++ b/.gitignore @@ -487,3 +487,5 @@ $RECYCLE.BIN/ # Intellij *.iml +*.db +/cli/src/Vdk/images diff --git a/cli/src/Vdk/Commands/UpdateClustersCommand.cs b/cli/src/Vdk/Commands/UpdateClustersCommand.cs index 0516016..c9a10af 100644 --- a/cli/src/Vdk/Commands/UpdateClustersCommand.cs +++ b/cli/src/Vdk/Commands/UpdateClustersCommand.cs @@ -13,18 +13,21 @@ public class UpdateClustersCommand : Command private readonly IKindClient _kind; private readonly IFileSystem _fileSystem; private readonly Func _clientFunc; + private readonly IReverseProxyClient _reverseProxy; public UpdateClustersCommand( IConsole console, IKindClient kind, IFileSystem fileSystem, - Func clientFunc) + Func clientFunc, + IReverseProxyClient reverseProxy) : base("clusters", "Update cluster configurations (certificates, etc.)") { _console = console; _kind = kind; _fileSystem = fileSystem; _clientFunc = clientFunc; + _reverseProxy = reverseProxy; var verboseOption = new Option("--verbose") { Description = "Enable verbose output for debugging" }; verboseOption.Aliases.Add("-v"); @@ -72,6 +75,20 @@ public async Task InvokeAsync(bool verbose = false) } _console.WriteLine("Cluster certificate update complete."); + + // Regenerate nginx configuration for all clusters (adds WebSocket support, etc.) + _console.WriteLine(); + if (_reverseProxy.Exists()) + { + _reverseProxy.RegenerateConfigs(); + } + else + { + if (verbose) + { + _console.WriteLine("[DEBUG] Reverse proxy not running, skipping nginx config regeneration."); + } + } } private async Task UpdateClusterCertificates(string clusterName, byte[] localCert, byte[] localKey, bool verbose) diff --git a/cli/src/Vdk/ConfigMounts/zot-config.json b/cli/src/Vdk/ConfigMounts/zot-config.json new file mode 100644 index 0000000..69ad82a --- /dev/null +++ b/cli/src/Vdk/ConfigMounts/zot-config.json @@ -0,0 +1,27 @@ +{ + "distSpecVersion": "1.1.0", + "storage": { + "rootDirectory": "/var/lib/registry", + "gc": true, + "gcDelay": "1h", + "gcInterval": "24h" + }, + "http": { + "address": "0.0.0.0", + "port": "5000" + }, + "log": { + "level": "info" + }, + "extensions": { + "ui": { + "enable": true + }, + "search": { + "enable": true, + "cve": { + "updateInterval": "24h" + } + } + } +} diff --git a/cli/src/Vdk/Constants/Containers.cs b/cli/src/Vdk/Constants/Containers.cs index 2dba5eb..c75b251 100644 --- a/cli/src/Vdk/Constants/Containers.cs +++ b/cli/src/Vdk/Constants/Containers.cs @@ -3,11 +3,11 @@ namespace Vdk.Constants; public static class Containers { public const string RegistryName = "vega-registry"; - public const string RegistryImage = "registry:2"; + public const string RegistryImage = "ghcr.io/project-zot/zot-linux-amd64:v2.1.0"; public const int RegistryContainerPort = 5000; - public const int RegistryHostPort = 50000; + public const int RegistryHostPort = 5000; public const string ProxyName = "vega-proxy"; - public const string ProxyImage = "nginx:latest"; + public const string ProxyImage = "nginx:1.27"; public const string CloudProviderKindName = "cloud-provider-kind"; public const string CloudProviderKindImage = "registry.k8s.io/cloud-provider-kind/cloud-controller-manager:v0.6.0"; } \ No newline at end of file diff --git a/cli/src/Vdk/Services/DockerHubClient.cs b/cli/src/Vdk/Services/DockerHubClient.cs index 2f536de..d7703ee 100644 --- a/cli/src/Vdk/Services/DockerHubClient.cs +++ b/cli/src/Vdk/Services/DockerHubClient.cs @@ -8,13 +8,30 @@ public class DockerHubClient(IDockerEngine docker, IConsole console) : IHubClien public void CreateRegistry() { if (ExistRegistry()) return; - console.WriteLine("Creating Vega VDK Registry"); + console.WriteLine("Creating Vega VDK Registry (Zot)"); console.WriteLine(" - This may take a few minutes..."); + + // Mount the config file and images directory for persistence + var configFile = new FileInfo(Path.Combine("ConfigMounts", "zot-config.json")); + var imagesDir = new DirectoryInfo("images"); + + // Ensure images directory exists + if (!imagesDir.Exists) + { + imagesDir.Create(); + } + + var volumes = new[] + { + new FileMapping { Source = configFile.FullName, Destination = "/etc/zot/config.json" }, + new FileMapping { Source = imagesDir.FullName, Destination = "/var/lib/registry" } + }; + docker.Run(Containers.RegistryImage, Containers.RegistryName, [PortMapping.DefaultRegistryPortMapping], null, - null, + volumes, null); } diff --git a/cli/src/Vdk/Services/IReverseProxyClient.cs b/cli/src/Vdk/Services/IReverseProxyClient.cs index 414860f..af1e4b5 100644 --- a/cli/src/Vdk/Services/IReverseProxyClient.cs +++ b/cli/src/Vdk/Services/IReverseProxyClient.cs @@ -13,4 +13,10 @@ public interface IReverseProxyClient void List(); bool Exists(); + + /// + /// Regenerates the nginx configuration for all VDK clusters. + /// Useful for applying config changes (like WebSocket support) to existing clusters. + /// + void RegenerateConfigs(); } \ No newline at end of file diff --git a/cli/src/Vdk/Services/ReverseProxyClient.cs b/cli/src/Vdk/Services/ReverseProxyClient.cs index baa8e2b..22d7629 100644 --- a/cli/src/Vdk/Services/ReverseProxyClient.cs +++ b/cli/src/Vdk/Services/ReverseProxyClient.cs @@ -175,6 +175,11 @@ private void PatchNginxConfig(string clusterName, int targetPortHttps) writer.WriteLine(" proxy_set_header X-Real-IP $remote_addr;"); writer.WriteLine(" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"); writer.WriteLine(" proxy_set_header X-Forwarded-Proto $scheme;"); + writer.WriteLine(); + writer.WriteLine(" # Support WebSocket protocol upgrades"); + writer.WriteLine(" proxy_http_version 1.1;"); + writer.WriteLine(" proxy_set_header Upgrade $http_upgrade;"); + writer.WriteLine(" proxy_set_header Connection \"upgrade\";"); writer.WriteLine(" }"); writer.WriteLine("}"); writer.WriteLine($"##### END {clusterName}"); @@ -415,6 +420,43 @@ public void List() throw new NotImplementedException(); } + public void RegenerateConfigs() + { + var conf = new FileInfo(NginxConf); + if (!conf.Exists) + { + _console.WriteWarning("Nginx configuration file does not exist. Run 'vega create proxy' first."); + return; + } + + _console.WriteLine("Regenerating nginx configuration for all VDK clusters..."); + + // Reinitialize the conf file with the hub server block + conf.Delete(); + InitConfFile(conf); + + // Iterate through all VDK clusters and add their server blocks + var clusters = _kind.ListClusters(); + var vdkClusters = clusters.Where(c => c.isVdk && c.master != null && c.master.HttpsHostPort.HasValue).ToList(); + + if (vdkClusters.Count == 0) + { + _console.WriteLine("No VDK clusters found to configure."); + ReloadConfigs(); + return; + } + + foreach (var cluster in vdkClusters) + { + _console.WriteLine($" Adding cluster '{cluster.name}' to nginx configuration"); + PatchNginxConfig(cluster.name, cluster.master!.HttpsHostPort!.Value); + } + + _console.WriteLine($"Regenerated configuration for {vdkClusters.Count} cluster(s)."); + ReloadConfigs(); + _console.WriteLine("Nginx configuration reloaded."); + } + private static int GetEnvironmentVariableAsInt(string variableName, int defaultValue = 0) { string strValue = Environment.GetEnvironmentVariable(variableName); diff --git a/cli/src/Vdk/Vdk.csproj b/cli/src/Vdk/Vdk.csproj index c451c7d..eda4e36 100644 --- a/cli/src/Vdk/Vdk.csproj +++ b/cli/src/Vdk/Vdk.csproj @@ -40,6 +40,13 @@ PreserveNewest + + PreserveNewest + + + + + diff --git a/cli/src/Vdk/vega.conf b/cli/src/Vdk/vega.conf index e69de29..ea4640c 100644 --- a/cli/src/Vdk/vega.conf +++ b/cli/src/Vdk/vega.conf @@ -0,0 +1,42 @@ + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name hub.dev-k8s.cloud; + ssl_certificate /etc/certs/fullchain.pem; + ssl_certificate_key /etc/certs/privkey.pem; + location / { + client_max_body_size 0; + proxy_pass http://host.docker.internal:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + + +##### START idp +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name idp.dev-k8s.cloud; + ssl_certificate /etc/certs/fullchain.pem; + ssl_certificate_key /etc/certs/privkey.pem; + location / { + proxy_pass https://host.docker.internal:40237; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Support WebSocket protocol upgrades + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +##### END idp +