Example #1
0
        public void Validate()
        {
            Provisioner = Provisioner ?? defaultProvisioner;
            DrivePrefix = DrivePrefix ?? defaultDrivePrefix;
            Setup       = Setup ?? new SetupOptions();
            Hosting     = Hosting ?? new HostingOptions();
            Vpn         = Vpn ?? new VpnOptions();
            HiveNode    = HiveNode ?? new HiveNodeOptions();
            Docker      = Docker ?? new DockerOptions();
            Image       = Image ?? new ImageOptions();
            Network     = Network ?? new NetworkOptions();
            Consul      = Consul ?? new ConsulOptions();
            Vault       = Vault ?? new VaultOptions();
            Log         = Log ?? new LogOptions();
            Dashboard   = Dashboard ?? new DashboardOptions();
            HiveFS      = HiveFS ?? new HiveFSOptions();
            Proxy       = Proxy ?? new ProxyOptions();
            HiveMQ      = HiveMQ ?? new HiveMQOptions();

            Setup.Validate(this);
            Network.Validate(this);
            Hosting.Validate(this);
            Vpn.Validate(this);
            HiveNode.Validate(this);
            Docker.Validate(this);
            Image.Validate(this);
            Consul.Validate(this);
            Vault.Validate(this);
            Log.Validate(this);
            Dashboard.Validate(this);
            HiveFS.Validate(this);
            Proxy.Validate(this);
            HiveMQ.Validate(this);

            new HostingManagerFactory().Validate(this);

            if (TimeSources == null || TimeSources.Length == 0 || TimeSources.Count(ts => string.IsNullOrWhiteSpace(ts)) > 0)
            {
                TimeSources = new string[] { "pool.ntp.org" };
            }

            if (NodeDefinitions == null || NodeDefinitions.Count == 0)
            {
                throw new HiveDefinitionException("At least one hive node must be defined.");
            }

            foreach (var node in NodeDefinitions.Values)
            {
                node.Validate(this);
            }

            if (Name == null)
            {
                throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(Name)}] property is required.");
            }

            if (!IsValidName(Name))
            {
                throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(Name)}={Name}] property is not valid.  Only letters, numbers, periods, dashes, and underscores are allowed.");
            }

            if (Datacenter == null)
            {
                throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(Datacenter)}] property is required.");
            }

            if (!IsValidName(Datacenter))
            {
                throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(Datacenter)}={Datacenter}] property is not valid.  Only letters, numbers, periods, dashes, and underscores are allowed.");
            }

            if (!string.IsNullOrEmpty(PackageProxy))
            {
                var packageCacheUris = PackageProxy.Split(',');

                for (int i = 0; i < packageCacheUris.Length; i++)
                {
                    packageCacheUris[i] = packageCacheUris[i].Trim();

                    if (!Uri.TryCreate(packageCacheUris[i], UriKind.Absolute, out var aptProxyUri))
                    {
                        throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(PackageProxy)}={PackageProxy}] includes [{packageCacheUris[i]}] which is not a valid URI.");
                    }

                    if (aptProxyUri.Scheme != "http")
                    {
                        throw new HiveDefinitionException($"The [{nameof(HiveDefinition)}.{nameof(PackageProxy)}={PackageProxy}] includes [{packageCacheUris[i]}] which does not have the [http] scheme.");
                    }
                }
            }

            var managementNodeCount = Managers.Count();

            if (managementNodeCount == 0)
            {
                throw new HiveDefinitionException("Hives must have at least one management node.");
            }
            else if (managementNodeCount > 5)
            {
                throw new HiveDefinitionException("Hives may not have more than [5] management nodes.");
            }
            else if (!NeonHelper.IsOdd(managementNodeCount))
            {
                throw new HiveDefinitionException("Hives must have an odd number of management nodes: [1, 3, or 5]");
            }

            // Ensure that each node has a valid unique or NULL IP address.

            NetworkCidr nodesSubnet   = null;
            NetworkCidr vpnPoolSubnet = null;

            if (Network.NodesSubnet != null)
            {
                nodesSubnet = NetworkCidr.Parse(Network.NodesSubnet);
            }

            if (Vpn.Enabled)
            {
                vpnPoolSubnet = NetworkCidr.Parse(Network.VpnPoolSubnet);
            }

            var addressToNode = new Dictionary <string, NodeDefinition>();

            foreach (var node in SortedNodes)
            {
                if (node.PrivateAddress != null)
                {
                    NodeDefinition conflictNode;

                    if (addressToNode.TryGetValue(node.PrivateAddress, out conflictNode))
                    {
                        throw new HiveDefinitionException($"Node [name={node.Name}] has invalid private IP address [{node.PrivateAddress}] that conflicts with node [name={conflictNode.Name}].");
                    }
                }
            }

            foreach (var node in SortedNodes)
            {
                if (node.PrivateAddress != null)
                {
                    if (!IPAddress.TryParse(node.PrivateAddress, out var address))
                    {
                        throw new HiveDefinitionException($"Node [name={node.Name}] has invalid private IP address [{node.PrivateAddress}].");
                    }

                    if (vpnPoolSubnet != null && vpnPoolSubnet.Contains(address))
                    {
                        throw new HiveDefinitionException($"Node [name={node.Name}] has private IP address [{node.PrivateAddress}] within the hosting [{nameof(Network.VpnPoolSubnet)}={Network.VpnPoolSubnet}].");
                    }

                    if (nodesSubnet != null && !nodesSubnet.Contains(address))
                    {
                        throw new HiveDefinitionException($"Node [name={node.Name}] has private IP address [{node.PrivateAddress}] that is not within the hosting [{nameof(Network.NodesSubnet)}={Network.NodesSubnet}].");
                    }
                }
                else if (!Hosting.IsCloudProvider)
                {
                    throw new HiveDefinitionException($"Node [name={node.Name}] is not assigned a private IP address.  This is required when deploying to a [{nameof(Environment)}={Environment}] hosting environment.");
                }
            }

            // Verify that we have nodes identified for persisting log data if logging is enabled.

            if (Log.Enabled)
            {
                if (Nodes.Where(n => n.Labels.LogEsData).Count() == 0)
                {
                    throw new HiveDefinitionException($"At least one node must be configured to store log data by setting [{nameof(NodeDefinition.Labels)}.{nameof(NodeLabels.LogEsData)}=true] when hive logging is enabled.");
                }
            }
        }
Example #2
0
        public void Validate()
        {
            // Wire up the node label parents.

            foreach (var node in NodeDefinitions.Values)
            {
                if (node.Labels != null)
                {
                    node.Labels.Node = node;
                }
            }

            // Validate the properties.

            Provisioner = Provisioner ?? defaultProvisioner;
            Kubernetes  = Kubernetes ?? new KubernetesOptions();
            Docker      = Docker ?? new DockerOptions();
            Ceph        = Ceph ?? new CephOptions()
            {
                Enabled = false
            };
            Mon = Mon ?? new MonOptions()
            {
                Enabled = false
            };
            Prometheus = Prometheus ?? new PrometheusOptions()
            {
                Enabled = false
            };
            DrivePrefix = DrivePrefix ?? defaultDrivePrefix;
            Setup       = Setup ?? new SetupOptions();
            Hosting     = Hosting ?? new HostingOptions();
            NodeOptions = NodeOptions ?? new NodeOptions();
            Network     = Network ?? new NetworkOptions();

            Kubernetes.Validate(this);
            Docker.Validate(this);
            Ceph.Validate(this);
            Mon.Validate(this);
            Prometheus.Validate(this);
            Setup.Validate(this);
            Network.Validate(this);
            Hosting.Validate(this);
            NodeOptions.Validate(this);
            Network.Validate(this);

            new HostingManagerFactory().Validate(this);

            if (TimeSources == null || TimeSources.Length == 0 || TimeSources.Count(ts => string.IsNullOrWhiteSpace(ts)) > 0)
            {
                TimeSources = new string[] { "pool.ntp.org" };
            }

            if (NodeDefinitions == null || NodeDefinitions.Count == 0)
            {
                throw new ClusterDefinitionException("At least one cluster node must be defined.");
            }

            foreach (var node in NodeDefinitions.Values)
            {
                node.Validate(this);
            }

            if (Name == null)
            {
                throw new ClusterDefinitionException($"The [{nameof(ClusterDefinition)}.{nameof(Name)}] property is required.");
            }

            if (!IsValidName(Name))
            {
                throw new ClusterDefinitionException($"The [{nameof(ClusterDefinition)}.{nameof(Name)}={Name}] property is not valid.  Only letters, numbers, periods, dashes, and underscores are allowed.");
            }

            if (Datacenter == null)
            {
                throw new ClusterDefinitionException($"The [{nameof(ClusterDefinition)}.{nameof(Datacenter)}] property is required.");
            }

            if (!IsValidName(Datacenter))
            {
                throw new ClusterDefinitionException($"The [{nameof(ClusterDefinition)}.{nameof(Datacenter)}={Datacenter}] property is not valid.  Only letters, numbers, periods, dashes, and underscores are allowed.");
            }

            var masterNodeCount = Masters.Count();

            if (masterNodeCount == 0)
            {
                throw new ClusterDefinitionException("Clusters must have at least one master node.");
            }
            else if (masterNodeCount > 5)
            {
                throw new ClusterDefinitionException("Clusters may not have more than [5] master nodes.");
            }
            else if (!NeonHelper.IsOdd(masterNodeCount))
            {
                throw new ClusterDefinitionException($"[{masterNodeCount}] master nodes is not allowed.  Only an off number of master nodes is allowed: [1, 3, or 5]");
            }

            if (!string.IsNullOrEmpty(PackageProxy))
            {
                // Ensure that this is set to zero or more network endpoints
                // formatted like:
                //
                //      HOSTNAME:PORT
                //      ADDRESS:PORT

                foreach (var endpoint in PackageProxy.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    var fields = endpoint.Split(':');

                    if (!IPAddress.TryParse(fields[0], out var address) && !NetHelper.IsValidHost(fields[0]))
                    {
                        throw new ClusterDefinitionException($"Invalid IP address or HOSTNAME [{fields[0]}] in [{nameof(ClusterDefinition)}.{nameof(PackageProxy)}={PackageProxy}].");
                    }

                    if (!int.TryParse(fields[1], out var port) || !NetHelper.IsValidPort(port))
                    {
                        throw new ClusterDefinitionException($"Invalid port [{fields[1]}] in [{nameof(ClusterDefinition)}.{nameof(PackageProxy)}={PackageProxy}].");
                    }
                }
            }

            // Ensure that each node has a valid unique or NULL IP address.

            NetworkCidr nodesSubnet = null;

            if (Network.NodeSubnet != null)
            {
                nodesSubnet = NetworkCidr.Parse(Network.NodeSubnet);
            }

            var addressToNode = new Dictionary <string, NodeDefinition>();

            foreach (var node in SortedNodes)
            {
                if (node.PrivateAddress != null)
                {
                    NodeDefinition conflictNode;

                    if (addressToNode.TryGetValue(node.PrivateAddress, out conflictNode))
                    {
                        throw new ClusterDefinitionException($"Node [name={node.Name}] has invalid private IP address [{node.PrivateAddress}] that conflicts with node [name={conflictNode.Name}].");
                    }
                }
            }

            foreach (var node in SortedNodes)
            {
                if (node.PrivateAddress != null)
                {
                    if (!IPAddress.TryParse(node.PrivateAddress, out var address))
                    {
                        throw new ClusterDefinitionException($"Node [name={node.Name}] has invalid private IP address [{node.PrivateAddress}].");
                    }

                    if (nodesSubnet != null && !nodesSubnet.Contains(address))
                    {
                        throw new ClusterDefinitionException($"Node [name={node.Name}] has private IP address [{node.PrivateAddress}] that is not within the hosting [{nameof(Network.NodeSubnet)}={Network.NodeSubnet}].");
                    }
                }
                else if (!Hosting.IsCloudProvider)
                {
                    throw new ClusterDefinitionException($"Node [name={node.Name}] is not assigned a private IP address.  This is required when deploying to a [{nameof(Environment)}={Environment}] hosting environment.");
                }
            }
        }