diff --git a/scripts/install.py b/scripts/install.py index ed9b1b328..8a480e1cf 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -235,6 +235,7 @@ def __init__(self, orchMode, debug=False, configOnly=False): self.checkPackageCmds = [] self.installPackageCmds = [] self.requiredPackages = [] + self.dockerComposeCmd = None self.pipCmd = 'pip3' if not which(self.pipCmd, debug=self.debug): @@ -302,15 +303,16 @@ def install_required_packages(self): return self.install_package(self.requiredPackages) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - def install_docker_images(self, docker_image_file): + def install_docker_images(self, docker_image_file, malcolm_install_path): result = False + composeFile = os.path.join(malcolm_install_path, 'docker-compose.yml') if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: if ( docker_image_file and os.path.isfile(docker_image_file) and InstallerYesOrNo( - f'Load Malcolm Docker images from {docker_image_file}', default=True, forceInteraction=True + f'Load Malcolm Docker images from {docker_image_file}?', default=True, forceInteraction=True ) ): ecode, out = self.run_process(['docker', 'load', '-q', '-i', docker_image_file], privileged=True) @@ -319,6 +321,31 @@ def install_docker_images(self, docker_image_file): else: eprint(f"Loading Malcolm Docker images failed: {out}") + elif ( + os.path.isfile(composeFile) + and self.dockerComposeCmd + and InstallerYesOrNo(f'Pull Malcolm Docker images?', default=True, forceInteraction=True) + ): + for priv in (False, True): + ecode, out = self.run_process( + [ + self.dockerComposeCmd, + '-f', + composeFile, + '--profile=malcolm', + 'pull', + '--quiet', + ], + privileged=priv, + ) + if ecode == 0: + break + + if ecode == 0: + result = True + else: + eprint(f"Pulling Malcolm Docker images failed: {out}") + return result # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -331,7 +358,7 @@ def install_malcolm_files(self, malcolm_install_file, default_config_dir): malcolm_install_file and os.path.isfile(malcolm_install_file) and InstallerYesOrNo( - f'Extract Malcolm runtime files from {malcolm_install_file}', default=True, forceInteraction=True + f'Extract Malcolm runtime files from {malcolm_install_file}?', default=True, forceInteraction=True ) ): # determine and create destination path for installation @@ -2593,24 +2620,28 @@ def install_docker_compose(self): if self.orchMode is OrchestrationFramework.DOCKER_COMPOSE: # first see if docker compose/docker-compose is already installed and runnable # (try non-root and root) - dockerComposeCmd = ('docker', 'compose') - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) + tmpComposeCmd = ('docker', 'compose') + + for priv in (False, True): + err, out = self.run_process([tmpComposeCmd, 'version'], privileged=priv) + if err == 0: + break if err != 0: - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) - if err != 0: - dockerComposeCmd = 'docker-compose' - if not which(dockerComposeCmd, debug=self.debug): - if os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): - dockerComposeCmd = '/usr/libexec/docker/cli-plugins/docker-compose' - elif os.path.isfile('/usr/local/bin/docker-compose'): - dockerComposeCmd = '/usr/local/bin/docker-compose' - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) - if err != 0: - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) + tmpComposeCmd = 'docker-compose' + if not which(tmpComposeCmd, debug=self.debug): + if os.path.isfile('/usr/libexec/docker/cli-plugins/docker-compose'): + tmpComposeCmd = '/usr/libexec/docker/cli-plugins/docker-compose' + elif os.path.isfile('/usr/local/bin/docker-compose'): + tmpComposeCmd = '/usr/local/bin/docker-compose' + for priv in (False, True): + err, out = self.run_process([tmpComposeCmd, 'version'], privileged=priv) + if err == 0: + break - if (err != 0) and InstallerYesOrNo( - 'docker compose failed, attempt to install docker compose?', default=True - ): + if err == 0: + self.dockerComposeCmd = tmpComposeCmd + + elif InstallerYesOrNo('docker compose failed, attempt to install docker compose?', default=True): if InstallerYesOrNo('Install docker compose directly from docker github?', default=True): # download docker-compose from github and put it in /usr/local/bin @@ -2624,7 +2655,7 @@ def install_docker_compose(self): unames.append(out[0].lower()) if len(unames) == 2: # download docker-compose from github and save it to a temporary file - tempFileName = os.path.join(self.tempDirName, dockerComposeCmd) + tempFileName = os.path.join(self.tempDirName, tmpComposeCmd) dockerComposeUrl = f"https://github.com/docker/compose/releases/download/v{DOCKER_COMPOSE_INSTALL_VERSION}/docker-compose-{unames[0]}-{unames[1]}" if DownloadToFile(dockerComposeUrl, tempFileName, debug=self.debug): os.chmod(tempFileName, 493) # 493 = 0o755, mark as executable @@ -2634,7 +2665,7 @@ def install_docker_compose(self): ) if err == 0: eprint("Download and installation of docker-compose apparently succeeded") - dockerComposeCmd = '/usr/local/bin/docker-compose' + tmpComposeCmd = '/usr/local/bin/docker-compose' else: raise Exception(f'Error copying {tempFileName} to /usr/local/bin: {out}') @@ -2658,11 +2689,13 @@ def install_docker_compose(self): eprint(f"Install docker-compose via pip failed with {err}, {out}") # see if docker-compose is now installed and runnable (try non-root and root) - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=False) - if err != 0: - err, out = self.run_process([dockerComposeCmd, 'version'], privileged=True) + for priv in (False, True): + err, out = self.run_process([tmpComposeCmd, 'version'], privileged=priv) + if err == 0: + break if err == 0: + self.dockerComposeCmd = tmpComposeCmd result = True if self.debug: eprint('docker compose succeeded') @@ -3908,8 +3941,6 @@ def main(): success = installer.install_docker_compose() if hasattr(installer, 'tweak_system_files'): success = installer.tweak_system_files() - if (orchMode is OrchestrationFramework.DOCKER_COMPOSE) and hasattr(installer, 'install_docker_images'): - success = installer.install_docker_images(imageFile) if (orchMode is OrchestrationFramework.DOCKER_COMPOSE) and hasattr(installer, 'install_malcolm_files'): success, installPath = installer.install_malcolm_files(malcolmFile, args.configDir is None) @@ -3960,12 +3991,19 @@ def main(): if args.debug: eprint(f"Malcolm installation detected at {installPath}") - if (installPath is not None) and os.path.isdir(installPath) and hasattr(installer, 'tweak_malcolm_runtime'): - installer.tweak_malcolm_runtime(installPath) - eprint(f"\nMalcolm has been installed to {installPath}. See README.md for more information.") - eprint( - f"Scripts for starting and stopping Malcolm and changing authentication-related settings can be found in {os.path.join(installPath, 'scripts')}." - ) + if (installPath is not None) and os.path.isdir(installPath): + if hasattr(installer, 'tweak_malcolm_runtime'): + installer.tweak_malcolm_runtime(installPath) + eprint(f"\nMalcolm has been installed to {installPath}. See README.md for more information.") + eprint( + f"Scripts for starting and stopping Malcolm and changing authentication-related settings can be found in {os.path.join(installPath, 'scripts')}." + ) + if ( + (not args.configOnly) + and (orchMode is OrchestrationFramework.DOCKER_COMPOSE) + and hasattr(installer, 'install_docker_images') + ): + success = installer.install_docker_images(imageFile, installPath) if __name__ == '__main__':