[bug] ship newer client discover script master
authorvarac <varacanero@zeromail.org>
Fri, 29 Jan 2016 22:21:32 +0000 (23:21 +0100)
committervarac <varacanero@zeromail.org>
Fri, 29 Jan 2016 22:21:50 +0000 (23:21 +0100)
than includes in squid-deb-proxy-client v. 0.8.13
to fix error messages being sent to stdout instead of stderr,
see https://bugs.launchpad.net/ubuntu/+source/squid-deb-proxy/+bug/1505670

files/client/apt-avahi-discover [new file with mode: 0755]
manifests/client.pp

diff --git a/files/client/apt-avahi-discover b/files/client/apt-avahi-discover
new file mode 100755 (executable)
index 0000000..8dbc1be
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+#
+# use avahi to find a _apt_proxy._tcp provider and return
+# a http proxy string suitable for apt
+
+import asyncore
+import functools
+import os
+import socket
+import sys
+import time
+from subprocess        import Popen, PIPE, call
+from syslog import syslog, LOG_INFO, LOG_USER
+
+DEFAULT_CONNECT_TIMEOUT_SEC = 2
+
+def DEBUG(msg):
+    if "--debug" in sys.argv:
+        sys.stderr.write(msg + "\n")
+
+
+def get_avahi_discover_timeout():
+    APT_AVAHI_TIMEOUT_VAR = "APT::Avahi-Discover::Timeout"
+    p = Popen(
+        ["/usr/bin/apt-config", "shell", "TIMEOUT", APT_AVAHI_TIMEOUT_VAR], 
+        stdout=PIPE)
+    stdout, stderr = p.communicate()
+    if not stdout:
+        DEBUG(
+            "no timeout set, using default '%s'" % DEFAULT_CONNECT_TIMEOUT_SEC)
+        return DEFAULT_CONNECT_TIMEOUT_SEC
+    if not stdout.startswith("TIMEOUT="):
+        raise ValueError("got unexpected apt-config output: '%s'" % stdout)
+    varname, sep, value = stdout.strip().partition("=")
+    timeout = int(value.strip("'"))
+    DEBUG("using timeout: '%s'" % timeout)
+    return timeout
+
+@functools.total_ordering
+class AptAvahiClient(asyncore.dispatcher):
+    def __init__(self, addr):
+        asyncore.dispatcher.__init__(self)
+        if is_ipv6(addr[0]):
+            self.create_socket(socket.AF_INET6, socket.SOCK_STREAM)
+            self.connect( (addr[0], addr[1], 0, 0) )
+        else:
+            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.connect(addr)
+        self._time_init = time.time()
+        self.time_to_connect = sys.maxint
+        self.address = addr
+    def handle_connect(self):
+        self.time_to_connect = time.time() - self._time_init
+        self.close()
+    def __eq__(self, other):
+        return self.time_to_connect == other.time_to_connect
+    def __lt__(self, other):
+        return self.time_to_connect < other.time_to_connect
+    def __repr__(self):
+        return "<%s> %s: %s" % (
+            self.__class__.__name__, self.addr, self.time_to_connect)
+    def log(self, message):
+        syslog((LOG_INFO|LOG_USER), '%s\n' % str(message))
+    def log_info(self, message, type='info'):
+        if type not in self.ignore_log_types:
+            self.log('%s: %s' % (type, message))
+
+
+def is_ipv6(a):
+    return ':' in a
+
+def is_linklocal(addr):
+    # Link-local should start with fe80 and six null bytes
+    return addr.startswith("fe80::")
+
+def get_proxy_host_port_from_avahi():
+    service = '_apt_proxy._tcp'
+
+    # Obtain all of the services addresses from avahi, pulling the IPv6
+    # addresses to the top.
+    addr4 = []
+    addr6 = []
+    p = Popen(['avahi-browse', '-kprtf', service], stdout=PIPE)
+    DEBUG("avahi-browse output:")
+    for line in p.stdout:
+        DEBUG(" '%s'" % line)
+        if line.startswith('='):
+            tokens = line.split(';')
+            addr = tokens[7]
+            port = int(tokens[8])
+            if is_ipv6(addr):
+                # We need to skip ipv6 link-local addresses since 
+                # APT can't use them
+                if not is_linklocal(addr):
+                    addr6.append((addr, port))
+            else:
+                addr4.append((addr, port))
+
+    # Run through the offered addresses and see if we we have a bound local
+    # address for it.
+    addrs = []
+    for (ip, port) in addr6 + addr4:
+        try:
+            res = socket.getaddrinfo(ip, port, 0, 0, 0, socket.AI_ADDRCONFIG)
+            if res:
+                addrs.append((ip, port))
+        except socket.gaierror:
+            pass
+    if not addrs:
+        return None
+    
+    # sort by answering speed
+    hosts = []
+    for addr in addrs:
+        hosts.append(AptAvahiClient(addr))
+    # 2s timeout, arbitray
+    timeout = get_avahi_discover_timeout()
+    asyncore.loop(timeout=timeout)
+    DEBUG("sorted hosts: '%s'" % sorted(hosts))
+
+    # No host wanted to connect
+    if (all(h.time_to_connect == sys.maxint for h in hosts)):
+        return None
+
+    fastest_host = sorted(hosts)[0]
+    fastest_address = fastest_host.address
+    return fastest_address
+
+
+if __name__ == "__main__":
+    # Dump the approved address out in an appropriate format.
+    address = get_proxy_host_port_from_avahi()
+    if address:
+        (ip, port) = address
+        if is_ipv6(ip):
+            print "http://[%s]:%s/" % (ip, port)
+        else:
+            print "http://%s:%s/" % (ip, port)
index 324544c..049f740 100644 (file)
@@ -1,5 +1,16 @@
+# install squid-deb-proxy-client package
 class squid_deb_proxy::client {
   package { 'squid-deb-proxy-client':
     ensure => installed,
+  } ->
+
+  # ship newer client discover script than includes in squid-deb-proxy-client
+  # v. 0.8.13 to fix error messages being sent to stdout instead of stderr,
+  # see https://bugs.launchpad.net/ubuntu/+source/squid-deb-proxy/+bug/1505670
+  file { '/usr/share/squid-deb-proxy-client/apt-avahi-discover':
+    source => 'puppet:///modules/squid_deb_proxy/client/apt-avahi-discover',
+    mode   => '0755',
+    owner  => 'root',
+    group  => 'root',
   }
 }