public TrafficRuleData(float x, float y, GameObject arrow, GameObject parent) { m_xy = new Vector2(x, y); m_rule = TrafficRule.Stop; m_arrow = GameObject.Instantiate(arrow); m_arrow.transform.SetParent(parent.transform); m_arrow.transform.localPosition = new Vector3(x, 0.01f, y); m_orgAngle = m_arrow.transform.localEulerAngles; m_visible = false; }
public void SetRule(Unit unit, TrafficRule rule) { if (unit.x < 0 || unit.y < 0 || unit.y >= m_length) { return; } Vector2 pos = unit.toVector2() + m_coord; if (m_list.ContainsKey(unit.x)) { var line = m_list[unit.x]; line.SetRule(pos.x, pos.y, rule); } else { var line = new TrafficRuleLine(pos.x, m_coord.y, m_length, m_arrow, m_parent); line.SetRule(pos.x, pos.y, rule); line.ShowArrows(m_visible); m_list[unit.x] = line; } }
public void SetRule(float x, float y, TrafficRule rule) { Unit unit = new Vector2(x - m_coord.x, y - m_coord.y).toUnit(); SetRule(unit, rule); }
void updateCacheForRoad(int index, TrafficRule rule) { if (rule == TrafficRule.Stop) { if (m_cacheForRoad.ContainsKey(index)) { m_cacheForRoad.Remove(index); } } else { if (!m_cacheForRoad.ContainsKey(index)) { RoadCache data = new RoadCache(); data.position = m_list[index].position; data.isUp = (rule & TrafficRule.Up) != 0; m_cacheForRoad[index] = data; } } }
public void SetRule(float x, float y, TrafficRule rule) { int index = getIndex(x, y); if (index != -1) { m_list[index].rule = rule; updateCacheForRoad(index, rule); } }
/// <inheritdoc/> public void Run(ModuleContext context) { TrafficManager trafficManager = null; bool isPublic = false; string name = null; string ruleName = null; bool deferUpdate = false; if (!context.ValidateArguments(context.Arguments, validModuleArgs)) { context.Failed = true; return; } // Obtain common arguments. context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [state]"); if (!context.Arguments.TryGetValue <string>("state", out var state)) { state = "present"; } state = state.ToLowerInvariant(); if (context.HasErrors) { return; } context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [name]"); if (!context.Arguments.TryGetValue <string>("name", out name)) { throw new ArgumentException($"[name] module argument is required."); } switch (name) { case "private": trafficManager = HiveHelper.Hive.PrivateTraffic; isPublic = false; break; case "public": trafficManager = HiveHelper.Hive.PublicTraffic; isPublic = true; break; default: throw new ArgumentException($"[name={name}] is not a one of the valid traffic manager names: [private] or [public]."); } if (state == "present" || state == "absent") { context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [rule_name]"); if (!context.Arguments.TryGetValue <string>("rule_name", out ruleName)) { throw new ArgumentException($"[rule_name] module argument is required."); } if (!HiveDefinition.IsValidName(ruleName)) { throw new ArgumentException($"[rule_name={ruleName}] is not a valid traffic manager rule name."); } context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [defer_update]"); if (!context.Arguments.TryGetValue <bool>("defer_update", out deferUpdate)) { deferUpdate = false; } } // We have the required arguments, so perform the operation. switch (state) { case "absent": context.WriteLine(AnsibleVerbosity.Trace, $"Check if rule [{ruleName}] exists."); if (trafficManager.GetRule(ruleName) != null) { context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] does exist."); context.WriteLine(AnsibleVerbosity.Info, $"Deleting rule [{ruleName}]."); if (context.CheckMode) { context.WriteLine(AnsibleVerbosity.Info, $"Rule [{ruleName}] will be deleted when CHECK-MODE is disabled."); } else { trafficManager.RemoveRule(ruleName, deferUpdate: deferUpdate); context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] deleted."); context.Changed = true; } } else { context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] does not exist."); } break; case "present": context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [rule]"); if (!context.Arguments.TryGetValue <JObject>("rule", out var routeObject)) { throw new ArgumentException($"[rule] module argument is required when [state={state}]."); } var ruleText = routeObject.ToString(); context.WriteLine(AnsibleVerbosity.Trace, "Parsing rule"); var newRule = TrafficRule.Parse(ruleText, strict: true); context.WriteLine(AnsibleVerbosity.Trace, "Rule parsed successfully"); // Use the name argument if the deserialized rule doesn't // have a name. This will make it easier on operators because // they won't need to specify the name twice. if (string.IsNullOrWhiteSpace(newRule.Name)) { newRule.Name = ruleName; } // Ensure that the name passed as an argument and the // name within the rule definition match. if (!string.Equals(ruleName, newRule.Name, StringComparison.InvariantCultureIgnoreCase)) { throw new ArgumentException($"The [rule_name={ruleName}] argument and the rule's [{nameof(TrafficRule.Name)}={newRule.Name}] property are not the same."); } context.WriteLine(AnsibleVerbosity.Trace, "Rule name matched."); // Validate the rule. context.WriteLine(AnsibleVerbosity.Trace, "Validating rule."); var proxySettings = trafficManager.GetSettings(); var validationContext = new TrafficValidationContext(name, proxySettings); // $hack(jeff.lill): // // This ensures that [proxySettings.Resolvers] is initialized with // the built-in Docker DNS resolver. proxySettings.Validate(validationContext); // Load the TLS certificates into the validation context so we'll // be able to verify that any referenced certificates mactually exist. // $todo(jeff.lill): // // This code assumes that the operator is currently logged in with // root Vault privileges. We'll have to do something else for // non-root logins. // // One idea might be to save two versions of the certificates. // The primary certificate with private key in Vault and then // just the public certificate in Consul and then load just // the public ones here. // // A good time to make this change might be when we convert to // use the .NET X.509 certificate implementation. if (!context.Login.HasVaultRootCredentials) { throw new ArgumentException("Access Denied: Root Vault credentials are required."); } context.WriteLine(AnsibleVerbosity.Trace, "Reading hive certificates."); using (var vault = HiveHelper.OpenVault(Program.HiveLogin.VaultCredentials.RootToken)) { // List the certificate key/names and then fetch each one // to capture details like the expiration date and covered // hostnames. foreach (var certName in vault.ListAsync("neon-secret/cert").Result) { context.WriteLine(AnsibleVerbosity.Trace, $"Reading: {certName}"); var certificate = vault.ReadJsonAsync <TlsCertificate>(HiveHelper.GetVaultCertificateKey(certName)).Result; validationContext.Certificates.Add(certName, certificate); } } context.WriteLine(AnsibleVerbosity.Trace, $"[{validationContext.Certificates.Count}] hive certificates downloaded."); // Actually perform the rule validation. newRule.Validate(validationContext); if (validationContext.HasErrors) { context.WriteLine(AnsibleVerbosity.Trace, $"[{validationContext.Errors.Count}] Route validation errors."); foreach (var error in validationContext.Errors) { context.WriteLine(AnsibleVerbosity.Important, error); context.WriteErrorLine(error); } context.Failed = true; return; } context.WriteLine(AnsibleVerbosity.Trace, "Rule is valid."); // Try reading any existing rule with this name and then determine // whether the two versions of the rule are actually different. context.WriteLine(AnsibleVerbosity.Trace, $"Looking for existing rule [{ruleName}]"); var existingRule = trafficManager.GetRule(ruleName); var changed = false; if (existingRule != null) { context.WriteLine(AnsibleVerbosity.Trace, $"Rule exists: checking for differences."); // Normalize the new and existing rules so the JSON text comparision // will work properly. newRule.Normalize(isPublic); existingRule.Normalize(isPublic); changed = !NeonHelper.JsonEquals(newRule, existingRule); if (changed) { context.WriteLine(AnsibleVerbosity.Trace, $"Rules are different."); } else { context.WriteLine(AnsibleVerbosity.Info, $"Rules are the same. No need to update."); } } else { changed = true; context.WriteLine(AnsibleVerbosity.Trace, $"Rule [name={ruleName}] does not exist."); } if (changed) { if (context.CheckMode) { context.WriteLine(AnsibleVerbosity.Info, $"Rule [{ruleName}] will be updated when CHECK-MODE is disabled."); } else { context.WriteLine(AnsibleVerbosity.Trace, $"Writing rule [{ruleName}]."); trafficManager.SetRule(newRule); context.WriteLine(AnsibleVerbosity.Info, $"Rule updated."); context.Changed = !context.CheckMode; } } break; case "update": trafficManager.Update(); context.Changed = true; context.WriteLine(AnsibleVerbosity.Info, $"Update signalled."); break; case "purge": var purgeItems = context.ParseStringArray("purge_list"); var purgeCaseSensitive = context.ParseBool("purge_case_sensitive"); if (!purgeCaseSensitive.HasValue) { purgeCaseSensitive = false; } if (purgeItems.Count == 0) { context.WriteLine(AnsibleVerbosity.Important, $"[purge_list] is missing or empty."); break; } trafficManager.Purge(purgeItems.ToArray(), caseSensitive: purgeCaseSensitive.Value); context.Changed = true; context.WriteLine(AnsibleVerbosity.Info, $"Purge request submitted."); break; default: throw new ArgumentException($"[state={state}] is not one of the valid choices: [present], [absent], or [update]."); } }
/// <inheritdoc/> public override void Run(CommandLine commandLine) { if (commandLine.HasHelpOption || commandLine.Arguments.Length == 0) { Console.WriteLine(usage); Program.Exit(0); } Program.ConnectHive(); // Process the command arguments. var trafficManager = (TrafficManager)null; var yaml = commandLine.HasOption("--yaml"); var directorName = commandLine.Arguments.FirstOrDefault(); var isPublic = false; switch (directorName) { case "help": // $hack: This isn't really a traffic manager name. Console.WriteLine(ruleHelp); Program.Exit(0); break; case "public": trafficManager = HiveHelper.Hive.PublicTraffic; isPublic = true; break; case "private": trafficManager = HiveHelper.Hive.PrivateTraffic; isPublic = false; break; default: Console.Error.WriteLine($"*** ERROR: Load balancer name must be one of [public] or [private] ([{directorName}] is not valid)."); Program.Exit(1); break; } commandLine = commandLine.Shift(1); var command = commandLine.Arguments.FirstOrDefault(); if (command == null) { Console.WriteLine(usage); Program.Exit(1); } commandLine = commandLine.Shift(1); string ruleName; switch (command) { case "get": ruleName = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(ruleName)) { Console.Error.WriteLine("*** ERROR: [RULE] argument expected."); Program.Exit(1); } if (!HiveDefinition.IsValidName(ruleName)) { Console.Error.WriteLine($"*** ERROR: [{ruleName}] is not a valid rule name."); Program.Exit(1); } // Fetch a specific traffic manager rule and output it. var rule = trafficManager.GetRule(ruleName); if (rule == null) { Console.Error.WriteLine($"*** ERROR: Load balancer [{directorName}] rule [{ruleName}] does not exist."); Program.Exit(1); } Console.WriteLine(yaml ? rule.ToYaml() : rule.ToJson()); break; case "haproxy": case "haproxy-bridge": case "varnish": // We're going to download the traffic manager's ZIP archive containing the // [haproxy.cfg] or [varnish.vcl] file, extract and write it to the console. using (var consul = HiveHelper.OpenConsul()) { var proxy = command.Equals("haproxy-bridge", StringComparison.InvariantCultureIgnoreCase) ? directorName + "-bridge" : directorName; var confKey = $"neon/service/neon-proxy-manager/proxies/{proxy}/proxy-conf"; var confZipBytes = consul.KV.GetBytesOrDefault(confKey).Result; if (confZipBytes == null) { Console.Error.WriteLine($"*** ERROR: Proxy ZIP configuration was not found in Consul at [{confKey}]."); Program.Exit(1); } using (var msZipData = new MemoryStream(confZipBytes)) { using (var zip = new ZipFile(msZipData)) { var file = command.Equals("varnish", StringComparison.InvariantCultureIgnoreCase) ? "varnish.vcl" : "haproxy.cfg"; var entry = zip.GetEntry(file); if (entry == null || !entry.IsFile) { Console.Error.WriteLine($"*** ERROR: Proxy ZIP configuration in Consul at [{confKey}] appears to be corrupt. Cannot locate the [{file}] entry."); Program.Exit(1); } using (var entryStream = zip.GetInputStream(entry)) { using (var reader = new StreamReader(entryStream)) { foreach (var line in reader.Lines()) { Console.WriteLine(line); } } } } } } break; case "inspect": Console.WriteLine(NeonHelper.JsonSerialize(trafficManager.GetDefinition(), Formatting.Indented)); break; case "list": case "ls": var showAll = commandLine.HasOption("--all"); var showSys = commandLine.HasOption("--sys"); var rules = trafficManager.ListRules( r => { if (showAll) { return(true); } else if (showSys) { return(r.System); } else { return(!r.System); } }); Console.WriteLine(); Console.WriteLine($"[{rules.Count()}] {trafficManager.Name} rules"); Console.WriteLine(); foreach (var item in rules) { Console.WriteLine(item.Name); } Console.WriteLine(); break; case "purge": var purgeUri = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(purgeUri)) { Console.Error.WriteLine("*** ERROR: [URI-PATTERN] or [ALL] argument expected."); } if (purgeUri.Equals("all", StringComparison.InvariantCultureIgnoreCase)) { if (!commandLine.HasOption("--force") && !Program.PromptYesNo($"*** Are you sure you want to purge all cached items for [{directorName.ToUpperInvariant()}]?")) { return; } trafficManager.PurgeAll(); } else { trafficManager.Purge(new string[] { purgeUri }); } Console.WriteLine(); Console.WriteLine("Purge request submitted."); Console.WriteLine(); break; case "update": trafficManager.Update(); break; case "remove": case "rm": ruleName = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(ruleName)) { Console.Error.WriteLine("*** ERROR: [RULE] argument expected."); Program.Exit(1); } if (!HiveDefinition.IsValidName(ruleName)) { Console.Error.WriteLine($"*** ERROR: [{ruleName}] is not a valid rule name."); Program.Exit(1); } if (trafficManager.RemoveRule(ruleName)) { Console.Error.WriteLine($"Deleted load balancer [{directorName}] rule [{ruleName}]."); } else { Console.Error.WriteLine($"*** ERROR: Load balancer [{directorName}] rule [{ruleName}] does not exist."); Program.Exit(1); } break; case "set": // $todo(jeff.lill): // // It would be really nice to download the existing rules and verify that // adding the new rule won't cause conflicts. Currently errors will be // detected only by the [neon-proxy-manager] which will log them and cease // updating the hive until the errors are corrected. // // An alternative would be to have some kind of service available in the // hive to do this for us or perhaps having [neon-proxy-manager] generate // a summary of all of the certificates (names, covered hostnames, and // expiration dates) and save this to Consul so it would be easy to // download. Perhaps do the same for the rules? if (commandLine.Arguments.Length != 2) { Console.Error.WriteLine("*** ERROR: FILE or [-] argument expected."); Program.Exit(1); } // Load the rule. Note that we support reading rules as JSON or // YAML, automatcially detecting the format. We always persist // rules as JSON though. var ruleFile = commandLine.Arguments[1]; string ruleText; if (ruleFile == "-") { using (var input = Console.OpenStandardInput()) { using (var reader = new StreamReader(input, detectEncodingFromByteOrderMarks: true)) { ruleText = reader.ReadToEnd(); } } } else { ruleText = File.ReadAllText(ruleFile); } var trafficManagerRule = TrafficRule.Parse(ruleText, strict: true); ruleName = trafficManagerRule.Name; if (!HiveDefinition.IsValidName(ruleName)) { Console.Error.WriteLine($"*** ERROR: [{ruleName}] is not a valid rule name."); Program.Exit(1); } // Validate a clone of the rule with any implicit frontends. var clonedRule = NeonHelper.JsonClone(trafficManagerRule); var context = new TrafficValidationContext(directorName, null) { ValidateCertificates = false // Disable this because we didn't download the certs (see note above) }; clonedRule.Validate(context); clonedRule.Normalize(isPublic); if (context.HasErrors) { Console.Error.WriteLine("*** ERROR: One or more rule errors:"); Console.Error.WriteLine(); foreach (var error in context.Errors) { Console.Error.WriteLine(error); } Program.Exit(1); } if (trafficManager.SetRule(trafficManagerRule)) { Console.WriteLine($"Load balancer [{directorName}] rule [{ruleName}] has been updated."); } else { Console.WriteLine($"Load balancer [{directorName}] rule [{ruleName}] has been added."); } break; case "settings": var settingsFile = commandLine.Arguments.FirstOrDefault(); if (string.IsNullOrEmpty(settingsFile)) { Console.Error.WriteLine("*** ERROR: [-] or FILE argument expected."); Program.Exit(1); } string settingsText; if (settingsFile == "-") { settingsText = NeonHelper.ReadStandardInputText(); } else { settingsText = File.ReadAllText(settingsFile); } var trafficManagerSettings = TrafficSettings.Parse(settingsText, strict: true); trafficManager.UpdateSettings(trafficManagerSettings); Console.WriteLine($"Traffic manager [{directorName}] settings have been updated."); break; case "status": using (var consul = HiveHelper.OpenConsul()) { var statusJson = consul.KV.GetStringOrDefault($"neon/service/neon-proxy-manager/status/{directorName}").Result; if (statusJson == null) { Console.Error.WriteLine($"*** ERROR: Status for traffic manager [{directorName}] is not currently available."); Program.Exit(1); } var trafficManagerStatus = NeonHelper.JsonDeserialize <TrafficStatus>(statusJson); Console.WriteLine(); Console.WriteLine($"Snapshot Time: {trafficManagerStatus.TimestampUtc} (UTC)"); Console.WriteLine(); using (var reader = new StringReader(trafficManagerStatus.Status)) { foreach (var line in reader.Lines()) { Console.WriteLine(line); } } } break; default: Console.Error.WriteLine($"*** ERROR: Unknown command: [{command}]"); Program.Exit(1); break; } }