/// <summary> /// Validates the node definition. /// </summary> /// <param name="clusterDefinition">The cluster definition.</param> /// <exception cref="ArgumentException">Thrown if the definition is not valid.</exception> public void Validate(ClusterDefinition clusterDefinition) { Covenant.Requires <ArgumentNullException>(clusterDefinition != null, nameof(clusterDefinition)); Vm = Vm ?? new VmNodeOptions(); var nodeDefinitionPrefix = $"{nameof(ClusterDefinition.NodeDefinitions)}"; // Ensure that the labels are wired up to the parent node. if (Labels == null) { Labels = new NodeLabels(this); } else { Labels.Node = this; } if (Name == null) { throw new ClusterDefinitionException($"The [{nodeDefinitionPrefix}.{nameof(Name)}] property is required."); } if (!ClusterDefinition.IsValidName(Name)) { throw new ClusterDefinitionException($"The [{nodeDefinitionPrefix}.{nameof(Name)}={Name}] property is not valid. Only letters, numbers, periods, dashes, and underscores are allowed."); } if (name == "localhost") { throw new ClusterDefinitionException($"The [{nodeDefinitionPrefix}.{nameof(Name)}={Name}] property is not valid. [localhost] is reserved."); } if (Name.StartsWith("neon-", StringComparison.InvariantCultureIgnoreCase) && !clusterDefinition.IsSpecialNeonCluster) { throw new ClusterDefinitionException($"The [{nodeDefinitionPrefix}.{nameof(Name)}={Name}] property is not valid because node names starting with [neon-] are reserved."); } if (name.Equals("cluster", StringComparison.InvariantCultureIgnoreCase)) { // $hack(jefflill): // // The node name [cluster] is reserved because we want to persist the // global cluster log file as [cluster.log] and we don't want this to // conflict with any of the node log files. // // See: KubeConst.ClusterSetupLogName throw new ClusterDefinitionException($"The [{nodeDefinitionPrefix}.{nameof(Name)}={Name}] property is not valid because the node name [cluster] is reserved."); } if (string.IsNullOrEmpty(Role)) { Role = NodeRole.Worker; } if (!Role.Equals(NodeRole.ControlPlane, StringComparison.InvariantCultureIgnoreCase) && !Role.Equals(NodeRole.Worker, StringComparison.InvariantCultureIgnoreCase)) { throw new ClusterDefinitionException($"[{nodeDefinitionPrefix}.{nameof(Name)}={Name}] has invalid [{nameof(Role)}={Role}]. This must be [{NodeRole.ControlPlane}] or [{NodeRole.Worker}]."); } // We don't need to check the node address for cloud providers. if (clusterDefinition.Hosting.IsOnPremiseProvider) { if (string.IsNullOrEmpty(Address)) { throw new ClusterDefinitionException($"[{nodeDefinitionPrefix}.{nameof(Name)}={Name}] requires [{nameof(Address)}] when hosting in an on-premise facility."); } if (!NetHelper.TryParseIPv4Address(Address, out var nodeAddress)) { throw new ClusterDefinitionException($"[{nodeDefinitionPrefix}.{nameof(Name)}={Name}] has invalid IP address [{Address}]."); } } switch (clusterDefinition.Hosting.Environment) { case HostingEnvironment.Aws: Aws = Aws ?? new AwsNodeOptions(); Aws.Validate(clusterDefinition, this.Name); break; case HostingEnvironment.Azure: Azure = Azure ?? new AzureNodeOptions(); Azure.Validate(clusterDefinition, this.Name); break; case HostingEnvironment.BareMetal: // No machine options to check at this time. break; case HostingEnvironment.Google: // $todo(jefflill: Implement this break; case HostingEnvironment.HyperV: case HostingEnvironment.XenServer: Vm = Vm ?? new VmNodeOptions(); Vm.Validate(clusterDefinition, this.Name); break; default: throw new NotImplementedException($"Hosting environment [{clusterDefinition.Hosting.Environment}] hosting option check is not implemented."); } }
/// <summary> /// Constructor. /// </summary> public NodeDefinition() { Labels = new NodeLabels(this); }
public void Validate(ClusterDefinition clusterDefinition) { Covenant.Requires <ArgumentNullException>(clusterDefinition != null); // Ensure that the labels are wired up to the parent node. if (Labels == null) { Labels = new NodeLabels(this); } else { Labels.Node = this; } if (Name == null) { throw new ClusterDefinitionException($"The [{nameof(NodeDefinition)}.{nameof(Name)}] property is required."); } if (!ClusterDefinition.IsValidName(Name)) { throw new ClusterDefinitionException($"The [{nameof(NodeDefinition)}.{nameof(Name)}={Name}] property is not valid. Only letters, numbers, periods, dashes, and underscores are allowed."); } if (name == "localhost") { throw new ClusterDefinitionException($"The [{nameof(NodeDefinition)}.{nameof(Name)}={Name}] property is not valid. [localhost] is reserved."); } if (Name.StartsWith("neon-", StringComparison.InvariantCultureIgnoreCase)) { throw new ClusterDefinitionException($"The [{nameof(NodeDefinition)}.{nameof(Name)}={Name}] property is not valid because node names starting with [node-] are reserved."); } if (clusterDefinition.Hosting.IsOnPremiseProvider) { if (string.IsNullOrEmpty(PrivateAddress)) { throw new ClusterDefinitionException($"Node [{Name}] requires [{nameof(PrivateAddress)}] when hosting in an on-premise facility."); } if (!IPAddress.TryParse(PrivateAddress, out var nodeAddress)) { throw new ClusterDefinitionException($"Node [{Name}] has invalid IP address [{PrivateAddress}]."); } } if (Azure != null) { Azure.Validate(clusterDefinition, this.Name); } if (clusterDefinition.Hosting.IsRemoteHypervisorProvider) { if (string.IsNullOrEmpty(VmHost)) { throw new ClusterDefinitionException($"Node [{Name}] does not specify a hypervisor [{nameof(NodeDefinition)}.{nameof(NodeDefinition.VmHost)}]."); } else if (clusterDefinition.Hosting.VmHosts.FirstOrDefault(h => h.Name.Equals(VmHost, StringComparison.InvariantCultureIgnoreCase)) == null) { throw new ClusterDefinitionException($"Node [{Name}] references hypervisor [{VmHost}] which is defined in [{nameof(HostingOptions)}={nameof(HostingOptions.VmHosts)}]."); } } if (VmMemory != null) { ClusterDefinition.ValidateSize(VmMemory, this.GetType(), nameof(VmMemory)); } if (VmDisk != null) { ClusterDefinition.ValidateSize(VmDisk, this.GetType(), nameof(VmDisk)); } }