Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5572956
[BREAKING] Bump Python to 3.12+
silviuburceadev Jun 23, 2026
423568e
[CLEAN-UP] Remove six package
silviuburceadev Jun 23, 2026
cfcb6fe
[CLEAN-UP] Remove pkg_resources references
silviuburceadev Jun 24, 2026
09d3be0
[CLEAN-UP] Replace distutils with stdlib (shutil) and packaging equiv…
silviuburceadev Jun 24, 2026
5bf4efe
[DEPS] Upgrade ansible from 8.7.0 to 11.13.0
silviuburceadev Jun 26, 2026
71223f4
[DEPS] Replace azure/msrestazure with azure-* modern packages
silviuburceadev Jun 26, 2026
605a760
[DEPS] Upgrade himl from 0.18.0 to 0.20.0
silviuburceadev Jun 26, 2026
d2e3039
[DEPS] Migrate from python-consul to py-consul 1.7.1
silviuburceadev Jun 26, 2026
465c5a4
[CLEAN-UP] Remove dead code from parser (_check_args_for_unicode)
silviuburceadev Jun 26, 2026
7fd235d
[DEPS] Upgrade urllib3 from 2.6.0 to 2.7.0
silviuburceadev Jun 26, 2026
8f9a12e
[DEPS] Pin hashmerge to 0.2 and passgen to 1.1.1
silviuburceadev Jun 28, 2026
db16f48
[DEPS] Upgrade awscli from 1.42.30 to 1.45.36 and boto3/botocore from…
silviuburceadev Jun 28, 2026
e21b202
[DEPS] Upgrade hvac from 1.2.1 to 2.4.0
silviuburceadev Jun 28, 2026
acf250a
[DEPS] Remove kubernetes package (unused)
silviuburceadev Jul 3, 2026
17eaed3
[BUGFIX] Python 2 leftovers clean-up
silviuburceadev Jul 6, 2026
34db834
[DEPS] Update Dockerfile dependencies
silviuburceadev Jul 6, 2026
f9dc2a8
[BUGFIX] Python 2 leftovers clean-up
silviuburceadev Jul 7, 2026
ea60c97
[DOCS] Add Python 3.13/3.14 in the compatibility list
silviuburceadev Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM python:3.12.3-alpine3.18 AS compile-image
ARG TARGETARCH
ARG TARGETPLATFORM
ARG TERRAFORM_VERSION="0.12.31"
ARG AZURE_CLI_VERSION="2.0.67"
ARG TERRAFORM_VERSION="0.15.5"
ARG AZURE_CLI_VERSION="2.87.0"

ENV BOTO_CONFIG=/dev/null
COPY . /sources/
Expand Down Expand Up @@ -40,10 +40,9 @@ RUN apk del --purge build
FROM python:3.12.3-alpine3.18
ARG TARGETARCH
ARG TARGETPLATFORM
ARG TERRAFORM_VERSION="0.12.31"
ARG VAULT_VERSION="1.1.3"
ARG KUBECTL_VERSION="v1.17.0"
ARG AWS_IAM_AUTHENTICATOR_VERSION="1.13.7/2019-06-11"
ARG TERRAFORM_VERSION="0.15.5"
ARG VAULT_VERSION="2.0.3"
ARG KUBECTL_VERSION="v1.34.1"
ARG HELM_VERSION="v3.16.3"
ARG HELM_FILE_VERSION="1.1.8"
ARG HELM_DIFF_VERSION="2.11.0%2B5"
Expand All @@ -63,7 +62,7 @@ RUN adduser ops -Du 2342 -h /home/ops \
&& ops --verbose -h \
&& apk del --purge build

RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl -O /usr/local/bin/kubectl \
RUN wget -q https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl -O /usr/local/bin/kubectl \
&& chmod +x /usr/local/bin/kubectl

RUN wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-${TARGETARCH}.tar.gz -O - | tar -xzO linux-${TARGETARCH}/helm > /usr/local/bin/helm \
Expand All @@ -76,9 +75,6 @@ RUN wget -q -O terraform.zip https://releases.hashicorp.com/terraform/${TERRAFOR
RUN wget -q -O vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_${TARGETARCH}.zip \
&& unzip vault.zip -d /usr/local/bin \
&& rm -rf vault.zip

RUN wget -q https://amazon-eks.s3-us-west-2.amazonaws.com/${AWS_IAM_AUTHENTICATOR_VERSION}/bin/linux/${TARGETARCH}/aws-iam-authenticator -O /usr/local/bin/aws-iam-authenticator \
&& chmod +x /usr/local/bin/aws-iam-authenticator

RUN wget -q https://github.com/helmfile/helmfile/releases/download/v${HELM_FILE_VERSION}/helmfile_${HELM_FILE_VERSION}_linux_${TARGETARCH}.tar.gz -O - | tar -xzO helmfile > /usr/local/bin/helmfile \
&& chmod +x /usr/local/bin/helmfile
Expand All @@ -94,7 +90,6 @@ WORKDIR /home/ops
USER root
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.9.11
RUN helm plugin install https://github.com/jkroepke/helm-secrets --version v3.8.2
RUN helm plugin install https://github.com/rimusz/helm-tiller # Obsolete in Helm 3


COPY --from=compile-image /azure-cli /home/ops/.local/azure-cli
Expand Down
31 changes: 16 additions & 15 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
simpledi==0.4.1
awscli==1.42.30
boto3==1.40.30
botocore==1.40.30
urllib3==2.6.0
ansible==8.7.0
azure-common==1.1.28
azure==4.0.0
msrestazure==0.6.4
awscli==1.45.36
boto3==1.43.36
botocore==1.43.36
urllib3==2.7.0
ansible==11.13.0
azure-identity==1.25.3
azure-mgmt-compute==38.1.0
azure-mgmt-network==30.2.0
azure-mgmt-resource==26.0.0
Jinja2==3.1.6
hashmerge
python-consul
hvac==1.2.1
passgen
hashmerge==0.2
py-consul==1.7.1
requests==2.34.2
hvac==2.4.0
passgen==1.1.1
inflection==0.5.1
kubernetes==33.1.0
himl==0.18.0
six
packaging==26.2
himl==0.20.0
GitPython==3.1.*
13 changes: 5 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
import os
import sys

try:
from setuptools import setup, find_packages
except ImportError:
from distutils.core import setup, find_packages
from setuptools import setup, find_packages

with open('README.md') as f:
_readme = f.read()
Expand All @@ -28,7 +25,7 @@
long_description=_readme + '\n\n',
long_description_content_type='text/markdown',
url='https://github.com/adobe/ops-cli',
python_requires='>=3.5',
python_requires='>=3.12',
author='Adobe',
author_email='noreply@adobe.com',
license='Apache2',
Expand All @@ -39,9 +36,9 @@
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
Expand Down
15 changes: 6 additions & 9 deletions src/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,30 @@
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.

import pkg_resources
import re
from distutils.version import StrictVersion
from importlib.metadata import version as importlib_version
from packaging.version import Version
from subprocess import call, Popen, PIPE

from six import PY3

from .cli import display


def validate_ops_version(min_ops_version):
current_ops_version = [
x.version for x in pkg_resources.working_set if x.project_name == "ops-cli"][0]
if StrictVersion(current_ops_version) < StrictVersion(min_ops_version):
current_ops_version = importlib_version('ops-cli')
if Version(current_ops_version) < Version(min_ops_version):
raise Exception("The current ops version {0} is lower than the minimum required version {1}. "
"Please upgrade by following the instructions seen here: "
"https://github.com/adobe/ops-cli#installing".format(current_ops_version, min_ops_version))


class Executor(object):
class Executor:
""" All cli commands usually return a dict(command=...) that will be executed by this handler"""

def __call__(self, result, pass_trough=True, cwd=None):
try:
return self._execute(result, pass_trough, cwd)
except Exception as ex:
display(str(ex) if PY3 else ex.message, stderr=True, color='red')
display(str(ex), stderr=True, color='red')
display(
'------- TRACEBACK ----------',
stderr=True,
Expand Down
11 changes: 4 additions & 7 deletions src/ops/ansible/filter_plugins/commonfilters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@
#OF ANY KIND, either express or implied. See the License for the specific language
#governing permissions and limitations under the License.

from __future__ import absolute_import
import os
from ops.cli import display
from six import iteritems


def read_file(fname):
if os.path.exists(fname):
Expand All @@ -38,7 +35,7 @@ def read_consul(key_path, consul_url="http://localhost:8500", recurse=True, show
ret = sc.get(key_path,recurse)
except Exception as e:
if show_error:
ret['error'] = e.message
ret['error'] = str(e)
return ret

def read_envvar(varname, default=None):
Expand All @@ -53,7 +50,7 @@ def read_yaml(fname, show_error=False):
ret = y.safe_load(f.read())
except Exception as e:
if show_error:
ret['error'] = e.message
ret['error'] = str(e)
return ret

def flatten_tree(d, parent_key='', sep='/'):
Expand Down Expand Up @@ -100,7 +97,7 @@ def write_vault(
namespace=None, mount_point=None, auto_prompt=auto_prompt)
new_data = {}
if isinstance(data, dict):
for k,v in iteritems(data):
for k, v in data.items():
new_data[k] = str(v)
elif key:
new_data[key] = str(data)
Expand Down Expand Up @@ -141,7 +138,7 @@ def escape_json(input):
return escaped[1:-1]
return escaped

class FilterModule(object):
class FilterModule:

def filters(self):
return {
Expand Down
4 changes: 2 additions & 2 deletions src/ops/ansible/vars_plugins/clusterconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ class VarsModule(BaseVarsPlugin):
def __init__(self, *args):
""" constructor """

super(VarsModule, self).__init__(*args)
super().__init__(*args)

logger.debug("Running plugin: %s with cluster config %s" % (__file__, os.environ['OPS_CLUSTER_CONFIG']))

app = AppContainer([os.environ['OPS_CLUSTER_CONFIG'], 'noop'])
self.config = app.cluster_config.all()

def get_vars(self, loader, path, entities, cache=True):
super(VarsModule, self).get_vars(loader, path, entities)
super().get_vars(loader, path, entities)
return self.config
4 changes: 2 additions & 2 deletions src/ops/ansible/vars_plugins/opsconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class VarsModule(BaseVarsPlugin):
def __init__(self, *args):
""" constructor """

super(VarsModule, self).__init__(*args)
super().__init__(*args)

logger.debug("Running plugin: %s with cluster config %s" % (__file__, os.environ['OPS_CLUSTER_CONFIG']))

Expand All @@ -37,5 +37,5 @@ def __init__(self, *args):
})

def get_vars(self, loader, path, entities, cache=True):
super(VarsModule, self).get_vars(loader, path, entities)
super().get_vars(loader, path, entities)
return self.config
2 changes: 1 addition & 1 deletion src/ops/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_config_value(config, key):
return config[key]
except KeyError as e:
err("You must set the %s value in %s.yaml or in the cli as an extra variable: -e %s=value" %
(e.message, config['cluster'], e.message))
(str(e), config['cluster'], str(e)))
sys.exit(1)

def check_if_teleport_binary_installed():
Expand Down
6 changes: 3 additions & 3 deletions src/ops/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def load_extra_vars(loader):
return extra_vars


class ClusterConfig(object):
class ClusterConfig:
def __init__(self, cluster_config_generator,
ops_config, cluster_config_path):
"""
Expand Down Expand Up @@ -130,7 +130,7 @@ def load_ssh_keys(self, cluster_config_path):
self.has_ssh_keys = False


class JinjaConfigGenerator(object):
class JinjaConfigGenerator:
def __init__(self, console_args, cluster_config_path, template):
self.cluster_config_path = cluster_config_path
self.console_args = console_args
Expand Down Expand Up @@ -162,7 +162,7 @@ def get(self):
return yaml.safe_load(rendered)


class ClusterConfigGenerator(object):
class ClusterConfigGenerator:
def __init__(self, console_args, cluster_config_path, template):
self.template = template
self.cluster_config_path = cluster_config_path
Expand Down
2 changes: 1 addition & 1 deletion src/ops/cli/config_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def get_epilog(self):
'''


class ConfigGeneratorRunner(object):
class ConfigGeneratorRunner:
def __init__(self, cluster_config_path):
self.cluster_config_path = cluster_config_path

Expand Down
4 changes: 2 additions & 2 deletions src/ops/cli/helmfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ def get_epilog(self):
'''


class HelmfileRunner(CompositionConfigGenerator, object):
class HelmfileRunner(CompositionConfigGenerator):
def __init__(self, ops_config, cluster_config_path, execute):
super(HelmfileRunner, self).__init__(["helmfiles"])
super().__init__(["helmfiles"])
logging.basicConfig(level=logging.INFO)
self.ops_config = ops_config
self.cluster_config_path = cluster_config_path
Expand Down
2 changes: 1 addition & 1 deletion src/ops/cli/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def configure(self, parser):
return parser


class InventoryRunner(object):
class InventoryRunner:
def __init__(self, ansible_inventory, cluster_name):
"""
:type ansible_inventory: ops.inventory.generator.AnsibleInventory
Expand Down
2 changes: 1 addition & 1 deletion src/ops/cli/packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_epilog(self):
'''


class PackerRunner(object):
class PackerRunner:
def __init__(self, root_dir, cluster_config):
self.cluster_config = cluster_config
self.root_dir = root_dir
Expand Down
29 changes: 2 additions & 27 deletions src/ops/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@

import argparse

import sys

from six import PY3


class RootParser(object):
class RootParser:
def __init__(self, sub_parsers=None):
"""
:type sub_parsers: list[SubParserConfig]
Expand Down Expand Up @@ -52,35 +48,14 @@ def _get_parser(self):

return parser

@staticmethod
def _check_args_for_unicode(args):
if args is None:
args = sys.argv

try:
for value in args:
if not PY3 and isinstance(value, unicode):
# Python3 or some Python3 compatibility mode can make
# arguments to be unicode, not str
value.encode('utf-8').encode('utf-8')
# Python 2 str, check if it can be represented in utf8
elif isinstance(value, str):
value.encode('utf-8')
except UnicodeDecodeError as e:
print('Invalid character in argument "{0}", most likely an "en dash", replace it with normal dash -'.format(
e.args[1]))
raise

def parse_args(self, args=None):
RootParser._check_args_for_unicode(args)
return self._get_parser().parse_args(args)

def parse_known_args(self, args=None):
RootParser._check_args_for_unicode(args)
return self._get_parser().parse_known_args(args)


class SubParserConfig(object):
class SubParserConfig:
def get_name(self):
pass

Expand Down
2 changes: 1 addition & 1 deletion src/ops/cli/playbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get_help(self):
return 'Run an Ansible playbook'


class PlaybookRunner(object):
class PlaybookRunner:
def __init__(self, ops_config, root_dir, inventory_generator,
cluster_config_path, cluster_config):
"""
Expand Down
2 changes: 1 addition & 1 deletion src/ops/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def get_name(self):
return 'run'


class CommandRunner(object):
class CommandRunner:

def __init__(self, ops_config, root_dir, inventory_generator,
cluster_config_path, cluster_config):
Expand Down
Loading