1+ using KubeOps . KubernetesClient ;
12using System . CommandLine ;
23using System . IO . Abstractions ;
4+ using k8s . Models ;
35using Vdk . Constants ;
4- using Vdk . Data ;
56using Vdk . Models ;
67using Vdk . Services ;
78using IConsole = Vdk . Services . IConsole ;
@@ -11,23 +12,39 @@ namespace Vdk.Commands;
1112public class CreateClusterCommand : Command
1213{
1314 private readonly IConsole _console ;
14- private readonly IEmbeddedDataReader _dataReader ;
15+ private readonly IKindVersionInfoService _kindVersionInfo ;
1516 private readonly IYamlObjectSerializer _yaml ;
1617 private readonly IFileSystem _fileSystem ;
1718 private readonly IKindClient _kind ;
19+ private readonly IHubClient _hub ;
1820 private readonly IFluxClient _flux ;
1921 private readonly IReverseProxyClient _reverseProxy ;
22+ private readonly Func < string , IKubernetesClient > _clientFunc ;
23+ private readonly GlobalConfiguration _configs ;
2024
21- public CreateClusterCommand ( IConsole console , IEmbeddedDataReader dataReader , IYamlObjectSerializer yaml , IFileSystem fileSystem , IKindClient kind , IFluxClient flux , IReverseProxyClient reverseProxy )
25+ public CreateClusterCommand (
26+ IConsole console ,
27+ IKindVersionInfoService kindVersionInfo ,
28+ IYamlObjectSerializer yaml ,
29+ IFileSystem fileSystem ,
30+ IKindClient kind ,
31+ IHubClient hub ,
32+ IFluxClient flux ,
33+ IReverseProxyClient reverseProxy ,
34+ Func < string , IKubernetesClient > clientFunc ,
35+ GlobalConfiguration configs )
2236 : base ( "cluster" , "Create a Vega development cluster" )
2337 {
2438 _console = console ;
25- _dataReader = dataReader ;
39+ _kindVersionInfo = kindVersionInfo ;
2640 _yaml = yaml ;
2741 _fileSystem = fileSystem ;
2842 _kind = kind ;
43+ _hub = hub ;
2944 _flux = flux ;
3045 _reverseProxy = reverseProxy ;
46+ _clientFunc = clientFunc ;
47+ _configs = configs ;
3148 var nameOption = new Option < string > ( new [ ] { "-n" , "--Name" } , ( ) => Defaults . ClusterName , "The name of the kind cluster to create." ) ;
3249 var controlNodes = new Option < int > ( new [ ] { "-c" , "--ControlPlaneNodes" } , ( ) => Defaults . ControlPlaneNodes , "The number of control plane nodes in the cluster." ) ;
3350 var workers = new Option < int > ( new [ ] { "-w" , "--Workers" } , ( ) => Defaults . WorkerNodes , "The number of worker nodes in the cluster." ) ;
@@ -39,9 +56,15 @@ public CreateClusterCommand(IConsole console, IEmbeddedDataReader dataReader, IY
3956 this . SetHandler ( InvokeAsync , nameOption , controlNodes , workers , kubeVersion ) ;
4057 }
4158
42- public async Task InvokeAsync ( string name = Defaults . ClusterName , int controlPlaneNodes = 1 , int workerNodes = 2 , string kubeVersion = Defaults . KubeApiVersion )
59+ public async Task InvokeAsync ( string name = Defaults . ClusterName , int controlPlaneNodes = 1 , int workerNodes = 2 , string ? kubeVersionRequested = null )
4360 {
44- var map = _dataReader . ReadJsonObjects < KindVersionMap > ( "Vdk.Data.KindVersionData.json" ) ;
61+ // check if the hub and proxy are there
62+ if ( ! _reverseProxy . Exists ( ) )
63+ _reverseProxy . Create ( ) ;
64+ if ( ! _hub . Exists ( ) )
65+ _hub . Create ( ) ;
66+
67+ var map = await _kindVersionInfo . GetVersionInfoAsync ( ) ;
4568 string ? kindVersion = null ;
4669 try
4770 {
@@ -52,13 +75,23 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
5275 _console . WriteError ( $ "Unable to retrieve the installed kind version. { ex . Message } ") ;
5376 return ;
5477 }
55-
5678 if ( string . IsNullOrWhiteSpace ( kindVersion ) )
5779 {
5880 _console . WriteWarning ( $ "Kind version { kindVersion } is not supported by the current VDK.") ;
5981 return ;
6082 }
83+ var kubeVersion = kubeVersionRequested ?? await _kindVersionInfo . GetDefaultKubernetesVersionAsync ( kindVersion ) ;
6184 var image = map . FindImage ( kindVersion , kubeVersion ) ;
85+ if ( image is null )
86+ {
87+ // If image is null the most likely cause is that the user has a newly released version of kind and
88+ // we have not yet downloaded the latest version info. To resolve this, we will attempt to circumvent
89+ // the cache timeout by directly calling UpdateAsync() and reloading the map. If it still doesn't
90+ // find it, then we are truly in an error state.
91+ await _kindVersionInfo . UpdateAsync ( ) ;
92+ map = await _kindVersionInfo . GetVersionInfoAsync ( ) ;
93+ image = map . FindImage ( kindVersion , kubeVersion ) ;
94+ }
6295 if ( image == null )
6396 {
6497 if ( map . FirstOrDefault ( m => m . Version == kindVersion ) is null )
@@ -85,7 +118,7 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
85118 {
86119 new ( )
87120 {
88- HostPath = _fileSystem . FileInfo . New ( "ConfigMounts/hosts.toml" ) . FullName ,
121+ HostPath = _fileSystem . FileInfo . New ( "ConfigMounts/hosts.toml" ) . FullName ,
89122 ContainerPath = "/etc/containerd/certs.d/hub.dev-k8s.cloud/hosts.toml"
90123 }
91124 } ;
@@ -94,7 +127,11 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
94127
95128 for ( int index = 0 ; index < workerNodes ; index ++ )
96129 {
97- cluster . Nodes . Add ( new KindNode ( ) { Role = "worker" , Image = image , ExtraMounts = new List < Mount >
130+ cluster . Nodes . Add ( new KindNode ( )
131+ {
132+ Role = "worker" ,
133+ Image = image ,
134+ ExtraMounts = new List < Mount >
98135 {
99136 new ( )
100137 {
@@ -107,7 +144,7 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
107144
108145 // add the containerd config patches
109146 cluster . ContainerdConfigPatches =
110- [ kindVersion == "0.27.0" ?
147+ [ kindVersion == "0.27.0" ?
111148 @"
112149[plugins.""io.containerd.cri.v1.images"".registry]
113150 config_path = ""/etc/containerd/certs.d""
@@ -120,13 +157,25 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
120157 ] ;
121158
122159 var manifest = _yaml . Serialize ( cluster ) ;
123- var path = _fileSystem . Path . GetTempFileName ( ) ;
160+ var path = _fileSystem . Path . Combine ( _fileSystem . Path . GetTempPath ( ) , _fileSystem . Path . GetRandomFileName ( ) ) ;
124161 await using ( var writer = _fileSystem . File . CreateText ( path ) )
125162 {
126163 // _console.WriteLine(path);
127164 await writer . WriteAsync ( manifest ) ;
128165 }
129166
167+ // if the name is not provided, and the default cluster name is used.. iterate the clusters to find the next available name
168+ if ( string . IsNullOrWhiteSpace ( name ) || name . ToLower ( ) == Defaults . ClusterName )
169+ {
170+ var clusters = _kind . ListClusters ( ) ;
171+ var i = 1 ;
172+ while ( clusters . Any ( x => x . name . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) )
173+ {
174+ name = $ "{ Defaults . ClusterName } -{ i } ";
175+ i ++ ;
176+ }
177+ }
178+
130179 _kind . CreateCluster ( name . ToLower ( ) , path ) ;
131180 var masterNode = cluster . Nodes . FirstOrDefault ( x => x . ExtraPortMappings ? . Any ( ) == true ) ;
132181 if ( masterNode == null )
@@ -140,6 +189,9 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
140189 {
141190 _reverseProxy . UpsertCluster ( name . ToLower ( ) , masterNode . ExtraPortMappings . First ( ) . HostPort ,
142191 masterNode . ExtraPortMappings . Last ( ) . HostPort ) ;
192+ var ns = _clientFunc ( name . ToLower ( ) ) . Get < V1Namespace > ( "vega-system" ) ;
193+ ns . EnsureMetadata ( ) . EnsureAnnotations ( ) [ _configs . MasterNodeAnnotation ] = _yaml . Serialize ( masterNode ) ;
194+ _clientFunc ( name . ToLower ( ) ) . Update ( ns ) ;
143195 }
144196 catch ( Exception e )
145197 {
@@ -148,6 +200,5 @@ public async Task InvokeAsync(string name = Defaults.ClusterName, int controlPla
148200 _console . WriteError ( "Failed to update reverse proxy: " + e . Message ) ;
149201 throw e ;
150202 }
151-
152203 }
153204}
0 commit comments