Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proposal for a binaries download page #190

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

fabianvf
Copy link
Contributor

No description provided.


## Drawbacks

- Requires us to bake all the binaries into the hub image. Could inflate the size and add complexity to the build process.
Copy link
Member

@jmontleon jmontleon Jul 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the openshift-console page that inspired this idea they seem to take a mixed approach, and we could probably duplicate either.

They have a separate downloads container that requests are redirected to, which contains all the oc binaries. This probably prevents having to complicate their console container build process, and likewise if we took a similar approach not complicate the hub container build process.

$ oc get po -n openshift-console
NAME                        READY   STATUS    RESTARTS   AGE
console-588ff9dbb7-shrnk    1/1     Running   11         7d2h
downloads-8ccff8dfd-frpx9   1/1     Running   10         7d2h
# find /usr/share/openshift/ -name oc
/usr/share/openshift/linux_amd64/oc
/usr/share/openshift/linux_arm64/oc
/usr/share/openshift/linux_ppc64le/oc
/usr/share/openshift/linux_s390x/oc
/usr/share/openshift/mac/oc
/usr/share/openshift/mac_arm64/oc

They then launch a python script.

For helm and odo they just redirect to the downloads.
https://mirror.openshift.com/pub/openshift-v4/clients/helm/latest
https://developers.redhat.com/content-gateway/rest/mirror/pub/openshift-v4/clients/odo/latest

I think either approach would be easier / better than baking them into the hub container.

The download container python script:

    Command:
      /bin/sh
    Args:
      -c
      cat <<EOF >>/tmp/serve.py
      import errno, http.server, os, re, signal, socket, sys, tarfile, tempfile, threading, time, zipfile
      
      signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit(0))
      
      def write_index(path, message):
        with open(path, 'wb') as f:
          f.write('\n'.join([
            '<!doctype html>',
            '<html lang="en">',
            '<head>',
            '  <meta charset="utf-8">',
            '</head>',
            '<body>',
            '  {}'.format(message),
            '</body>',
            '</html>',
            '',
          ]).encode('utf-8'))
      
      # Launch multiple listeners as threads
      class Thread(threading.Thread):
        def __init__(self, i, socket):
          threading.Thread.__init__(self)
          self.i = i
          self.socket = socket
          self.daemon = True
          self.start()
      
        def run(self):
          server = http.server.SimpleHTTPRequestHandler
          server.server_version = "OpenShift Downloads Server"
          server.sys_version = ""
          httpd = http.server.HTTPServer(addr, server, False)
      
          # Prevent the HTTP server from re-binding every handler.
          # https://stackoverflow.com/questions/46210672/
          httpd.socket = self.socket
          httpd.server_bind = self.server_close = lambda self: None
      
          httpd.serve_forever()
      
      temp_dir = tempfile.mkdtemp()
      print('serving from {}'.format(temp_dir))
      os.chdir(temp_dir)
      for arch in ['amd64', 'arm64', 'ppc64le', 's390x']:
        os.mkdir(arch)
      content = ['<a href="oc-license">license</a>']
      os.symlink('/usr/share/openshift/LICENSE', 'oc-license')
      
      for arch, operating_system, path in [
          ('amd64', 'linux', '/usr/share/openshift/linux_amd64/oc'),
          ('amd64', 'mac', '/usr/share/openshift/mac/oc'),
          ('amd64', 'windows', '/usr/share/openshift/windows/oc.exe'),
          ('arm64', 'linux', '/usr/share/openshift/linux_arm64/oc'),
          ('arm64', 'mac', '/usr/share/openshift/mac_arm64/oc'),
          ('ppc64le', 'linux', '/usr/share/openshift/linux_ppc64le/oc'),
          ('s390x', 'linux', '/usr/share/openshift/linux_s390x/oc'),
          ]:
        basename = os.path.basename(path)
        target_path = os.path.join(arch, operating_system, basename)
        os.mkdir(os.path.join(arch, operating_system))
        os.symlink(path, target_path)
        base_root, _ = os.path.splitext(basename)
        archive_path_root = os.path.join(arch, operating_system, base_root)
        with tarfile.open('{}.tar'.format(archive_path_root), 'w') as tar:
          tar.add(path, basename)
        with zipfile.ZipFile('{}.zip'.format(archive_path_root), 'w') as zip:
          zip.write(path, basename)
        content.append(
          '<a href="{0}">oc ({1} {2})</a> (<a href="{3}.tar">tar</a> <a href="{3}.zip">zip</a>)'.format(
            target_path, arch, operating_system, archive_path_root
          )
        )
      
      for root, directories, filenames in os.walk(temp_dir):
        root_link = os.path.relpath(temp_dir, os.path.join(root, 'child')).replace(os.path.sep, '/')
        for directory in directories:
          write_index(
            path=os.path.join(root, directory, 'index.html'),
            message='<p>Directory listings are disabled.  See <a href="{}">here</a> for available content.</p>'.format(root_link),
          )
      
      write_index(
        path=os.path.join(temp_dir, 'index.html'),
        message='\n'.join(
          ['<ul>'] +
          ['  <li>{}</li>'.format(entry) for entry in content] +
          ['</ul>']
        ),
      )
      
      # Create socket
      # IPv6 should handle IPv4 passively so long as it is not bound to a
      # specific address or set to IPv6_ONLY
      # https://stackoverflow.com/questions/25817848/python-3-does-http-server-support-ipv6
      try:
        addr = ('::', 8080)
        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
      except socket.error as err:
        # errno.EAFNOSUPPORT is "socket.error: [Errno 97] Address family not supported by protocol"
        # When IPv6 is disabled, socket will bind using IPv4.
        if err.errno == errno.EAFNOSUPPORT:
          addr = ('', 8080)
          sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        else:
          raise    
      sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
      sock.bind(addr)
      sock.listen(5)
      
      [Thread(i, socket=sock) for i in range(100)]
      time.sleep(9e9)
      EOF
      exec python3 /tmp/serve.py

Signed-off-by: Fabian von Feilitzsch <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants