diff --git a/cf_remote/commands.py b/cf_remote/commands.py index f093a37..53bc9a4 100644 --- a/cf_remote/commands.py +++ b/cf_remote/commands.py @@ -314,13 +314,22 @@ def _iterate_over_packages( download=False, output_dir=None, insecure=False, + allow_expired=False, ): assert edition in ["enterprise", "community", None] releases = Releases(edition) print("Available releases: {}".format(releases)) + if allow_expired: + print("Expired releases: {}".format(releases.show_expired())) release_versions = [rel.version for rel in releases.releases] - if version and version not in release_versions: + expired_versions = [rel.version for rel in releases.expired_releases] + + using_expired_version = version and (version in expired_versions) + + if using_expired_version and not allow_expired: + raise CFRUserError("Use flag --allow-expired to list expired version") + elif not using_expired_version and (version not in release_versions): raise CFRExitError("CFEngine version '%s' doesn't exist (yet)." % version) if tags and not version: @@ -336,6 +345,12 @@ def _iterate_over_packages( if not release: raise CFRExitError("Failed to find a release for version '%s'" % version) print("Using {}:".format(release)) + if using_expired_version: + log.warning( + "You are using an expired CFEngine version {} which is no longer supported and may contain known vulnerabilities. Proceed at your own risk.".format( + version + ) + ) log.debug("Looking for a release based on host tags: {}".format(tags)) artifacts = release.find(tags) if len(artifacts) == 0: @@ -368,16 +383,33 @@ def _iterate_over_packages( print("Copied to '{}' (Checksum OK).".format(output_path)) else: print(artifact.url) + if using_expired_version: + log.warning( + "You are using an expired CFEngine version {} which is no longer supported and may contain known vulnerabilities. Proceed at your own risk.".format( + version + ) + ) return 0 # named list_command to not conflict with list() -def list_command(tags=None, version=None, edition=None): - return _iterate_over_packages(tags, version, edition, False) +def list_command(tags=None, version=None, edition=None, allow_expired=False): + return _iterate_over_packages( + tags, version, edition, False, allow_expired=allow_expired + ) -def download(tags=None, version=None, edition=None, output_dir=None, insecure=False): - return _iterate_over_packages(tags, version, edition, True, output_dir, insecure) +def download( + tags=None, + version=None, + edition=None, + output_dir=None, + insecure=False, + allow_expired=False, +): + return _iterate_over_packages( + tags, version, edition, True, output_dir, insecure, allow_expired + ) def _get_aws_creds_from_env(): diff --git a/cf_remote/main.py b/cf_remote/main.py index 187e4c9..2ff1026 100644 --- a/cf_remote/main.py +++ b/cf_remote/main.py @@ -135,6 +135,10 @@ def _get_arg_parser(): ) sp.add_argument("tags", metavar="TAG", nargs="*") + sp.add_argument( + "--allow-expired", help="Also lists expired packages", action="store_true" + ) + sp = subp.add_parser("download", help="Download CFEngine packages") sp.add_argument( "--edition", @@ -152,6 +156,9 @@ def _get_arg_parser(): help="Ignore mismatching checksums when downloading urls", action="store_true", ) + sp.add_argument( + "--allow-expired", help="Allow expired packages", action="store_true" + ) sp = subp.add_parser( "run", help="Run the command given as arguments on the given hosts" @@ -352,11 +359,16 @@ def run_command_with_args(command, args) -> int: "packages command is deprecated, please use the new command: download" ) return commands.download( - tags=args.tags, version=args.version, edition=args.edition + tags=args.tags, + version=args.version, + edition=args.edition, ) elif command == "list": return commands.list_command( - tags=args.tags, version=args.version, edition=args.edition + tags=args.tags, + version=args.version, + edition=args.edition, + allow_expired=args.allow_expired, ) elif command == "download": return commands.download( @@ -365,6 +377,7 @@ def run_command_with_args(command, args) -> int: edition=args.edition, output_dir=args.output_dir, insecure=args.insecure, + allow_expired=args.allow_expired, ) elif command == "run": return commands.run(hosts=args.hosts, raw=args.raw, command=args.remote_command) diff --git a/cf_remote/packages.py b/cf_remote/packages.py index a2ad7ce..8eed3fa 100644 --- a/cf_remote/packages.py +++ b/cf_remote/packages.py @@ -205,6 +205,7 @@ def __init__(self, edition: Union[str, None] = "enterprise"): self.url = "https://cfengine.com/release-data/{}/releases.json".format(edition) self.data = get_json(self.url) self.supported_branches = [] + self.expired_branches = [] for branch in self.data["lts_branches"]: expires = branch["supported_until"] expires += "-25" @@ -216,8 +217,10 @@ def __init__(self, edition: Union[str, None] = "enterprise"): log.info( "LTS branch {} expired on {}".format(branch["branch_name"], expires) ) + self.expired_branches.append(branch["branch_name"]) self.releases = [] + self.expired_releases = [] for release in self.data["releases"]: rel = Release(release) if "status" in release and release["status"] == "unsupported": @@ -228,6 +231,12 @@ def __init__(self, edition: Union[str, None] = "enterprise"): and ("latest_stable" not in release) ): continue + if ( + "lts_branch" in release + and release["lts_branch"] in self.expired_branches + ): + self.expired_releases.append(rel) + continue if ( "lts_branch" in release and release["lts_branch"] not in self.supported_branches @@ -246,5 +255,8 @@ def pick_version(self, version): return Release(release) return None + def show_expired(self): + return ", ".join(str(x.version) for x in self.expired_releases) + def __str__(self): return ", ".join(str(x.version) for x in self.releases)