/// <summary> /// Lists the traffic manager rules. /// </summary> /// <param name="predicate">Optional predicate used to filter the output rules.</param> /// <returns>The <see cref="IEnumerable{T}"/> of traffic manager rules.</returns> public IEnumerable <TrafficRule> ListRules(Func <TrafficRule, bool> predicate = null) { var rulesResponse = hive.Consul.Client.KV.ListOrDefault <JObject>($"{proxyManagerPrefix}/conf/{Name}/rules/").Result; if (rulesResponse != null) { var rules = new List <TrafficRule>(); foreach (var rulebject in rulesResponse) { var rule = TrafficRule.ParseJson(rulebject.ToString()); if (predicate == null || predicate(rule)) { rules.Add(rule); } } return(rules); } else { return(new TrafficRule[0]); } }
/// <summary> /// Validates the header. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficRule rule) { if (string.IsNullOrWhiteSpace(Name)) { context.Error($"Rule [{rule.Name}] specifies a NULL or empty [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Name)}]."); } foreach (var ch in Name) { if (char.IsLetterOrDigit(ch) || ch == '-' || ch == '_') { continue; } context.Error($"Rule [{rule.Name}] specifies a [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Name)}] with the invalid character [{ch}]."); break; } if (string.IsNullOrWhiteSpace(Value)) { context.Error($"Rule [{rule.Name}] specifies a NULL [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Value)}]."); } // $todo(jeff.lill): // // We could try to validate the [Value] property too (e.g. to ensure that it doesn't include "\r\n") but // that could be overly restrictive if I'm not careful. I'm going to leave this be for now. }
/// <summary> /// Validates the frontend. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficRule rule) { // Verify [MaxConnections] if (MaxConnections < 0) { context.Error($"Rule [{rule.Name}] specifies invalid [{nameof(MaxConnections)}={MaxConnections}]."); } }
/// <summary> /// Returns a traffic manager rule if it exists. /// </summary> /// <param name="ruleName">The rule name.</param> /// <returns>The <see cref="TrafficRule"/> or <c>null</c>.</returns> public TrafficRule GetRule(string ruleName) { Covenant.Requires <ArgumentException>(HiveDefinition.IsValidName(ruleName)); var ruleKey = GetProxyRuleKey(ruleName); if (hive.Consul.Client.KV.Exists(ruleKey).Result) { return(TrafficRule.ParseJson(hive.Consul.Client.KV.GetString(ruleKey).Result)); } else { return(null); } }
/// <summary> /// Adds or updates a traffic manager rule. /// </summary> /// <param name="rule">The rule definition.</param> /// <param name="deferUpdate"> /// <para> /// Optionally defers expicitly notifying the <b>neon-proxy-manager</b> of the /// change until <see cref="Update()"/> is called or the <b>neon-proxy-manager</b> /// performs the periodic check for changes (which defaults to 60 seconds). You /// may consider passing <paramref name="deferUpdate"/><c>=true</c> when you are /// modifying a multiple rules at the same time to avoid making the proxy manager /// and proxy instances handle each rule change individually. /// </para> /// <para> /// Instead, you could pass <paramref name="deferUpdate"/><c>=true</c> for all of /// the rule changes and then call <see cref="Update()"/> afterwards. /// </para> /// </param> /// <returns> /// <c>true</c> if it existed and was updated, <b>false</b> /// if the traffic manager rule didn't already exist and was added. /// </returns> /// <exception cref="HiveDefinitionException">Thrown if the rule is not valid.</exception> public bool SetRule(TrafficRule rule, bool deferUpdate = false) { Covenant.Requires <ArgumentNullException>(rule != null); Covenant.Requires <ArgumentNullException>(HiveDefinition.IsValidName(rule.Name)); if (!IsPublic) { // Ensure that the [PublicPort] is disabled for non-public rules // just to be absolutely sure that these endpoints are not exposed // to the Internet for cloud deployments and to avoid operators // being freaked out if they see a non-zero port here. var httpRule = rule as TrafficHttpRule; if (httpRule != null) { foreach (var frontEnd in httpRule.Frontends) { frontEnd.PublicPort = 0; } } else { var tcpRule = rule as TrafficTcpRule; if (tcpRule != null) { foreach (var frontEnd in tcpRule.Frontends) { frontEnd.PublicPort = 0; } } } } // $todo(jeff.lill): // // We're going to minimially ensure that the rule is valid. It would // be better to do full server side validation. var context = new TrafficValidationContext(Name, GetSettings()) { ValidateCertificates = false, // Disable this because we didn't download the certs. ValidateResolvers = false }; rule.Validate(context); context.ThrowIfErrors(); // Publish the rule. var ruleKey = GetProxyRuleKey(rule.Name); var update = hive.Consul.Client.KV.Exists(ruleKey).Result; // Load the full proxy definition and hive certificates, add/replace // the rule being set and then verify that the rule is OK. var proxyDefinition = GetDefinition(); var certificates = hive.Certificate.GetAll(); proxyDefinition.Rules[rule.Name] = rule; proxyDefinition.Validate(certificates); var validationContext = proxyDefinition.Validate(certificates); validationContext.ThrowIfErrors(); // Save the rule to the hive and signal that the // load balancers need to be updated. hive.Consul.Client.KV.PutObject(ruleKey, rule, Formatting.Indented).Wait(); if (!deferUpdate) { Update(); } return(update); }