def route_monitoring(yml: object): """ A route should always be monitored by ormon. annotations: thobits.com/ormon-valid-statuscodes: 200,300 thobits.com/ormon-body-regex: Thomann note: ormon-valid-statuscodes has to be a string. """ matches = json_path('metadata.annotations."thobits.com/ormon-skip"').find( yml) if (matches and matches[0].value in ['1', 't', 'T', 'TRUE', 'true' or 'True']): click.secho( f'* resource with disabled monitoring found: {object_ident(yml)}', fg='yellow') return codes = json_path('metadata.annotations."thobits.com/"').find(yml) if codes: codes = codes.pop().value if not all(code.isdigit() for code in codes.split(',')): raise LintError(f'invalid statuscodes for {object_ident(yml)}') return body_regexes = json_path( 'metadata.annotations."thobits.com/ormon-body-regex"').find(yml) if body_regexes: body_regex = body_regexes.pop() return raise LintError(f'no monitoring specified for {object_ident(yml)}')
def named_velero_for(yml: object): """ Validates that a backup or a backup-exclude is defined for each volume. annotations: backup.velero.io/backup-volumes: data,backup backup.velero.io/backup-volumes-excludes: cache note: all defined volumes must specify a backup """ ident = object_ident(yml) backup_matches = json_path( 'spec.template.metadata.annotations.' '"backup.velero.io/backup-volumes"').find(yml) skip_matches = json_path( 'spec.template.metadata.annotations.' '"backup.velero.io/backup-volumes-excludes"').find(yml) if not backup_matches: raise LintError(f"unable to find velero annotation on {ident}") backupped_volumes = backup_matches[0].value.split(',') skipped_volumes = [] if skip_matches: skipped_volumes = skip_matches[0].value.split(',') if name in skipped_volumes: click.secho(f"* backup for volume {name} skipped in {ident}", fg='yellow') elif name in backupped_volumes: click.secho(f"* backup for volume {name} found in {ident}", fg='green') else: raise LintError(f"no backup or skip for {name} found in {ident}")
def podhandler_pod_disruption_budget(yml: object): """require a podhandler for every deployment/statefulset.""" matches = json_path('spec.template.metadata.labels').find(yml) must_validators = [] for match in matches: must_validators.append(pdb_for(object_ident(yml), match.value)) return ([], must_validators)
def named_pdb_for(yml: object): """ Validates that a matching PodDisruptionBudget for the deployments labels exists. # example: apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: myservice-pdb labels: app: myservice {{- include "helm.labels" . | nindent 4 }} spec: maxUnavailable: 1 selector: matchLabels: app: myservice Replace all myservice with service name / other labels. """ ident = object_ident(yml) matches = json_path('spec.selector.matchLabels').find(yml) if not matches: raise LintError(f"* no label matching {name} for {ident}") got_labels = matches[0].value for key, value in got_labels.items(): if key not in labels.keys() or labels[key] != value: message = ( f"pdb {ident} is missing label combination for {name}" f"{key}={value}") raise LintError(message) click.secho(f"* found pdb {ident} for {name}", fg='green')
def backup_stateful(yml: object): """Enforce backup annotations for StatefulSet volumeClaimTemplates.""" matches = json_path('spec.volumeClaimTemplates.[*]').find(yml) must_validators = [] for match in matches: name = match.value.get('metadata', {}).get('name') must_validators.append(velero_for(name)) return ([], must_validators)
def podhandler_oauth(yml: object): """require a route with reencrypt policy if oauth was found enabled.""" matches = json_path('spec.template.spec.containers.[*]').find(yml) gatekeepers = [m for m in matches if 'gatekeeper' in m.value.get('image', '')] must_validators = [] if gatekeepers: must_validators.append( route_for('gatekeeper', {'spec.tls.termination': 'Reencrypt'}) ) return ([], must_validators)
def podhandler_git_sha(yml: object): """ This resource requires a git-sha annotation to prevent issues on rolling updates with old configmap and secret versions. This annotation must be set on "spec.template.metadata.annotations". annotations: thobits.com/git-sha: 000000 """ ident = object_ident(yml) matches_bad = json_path( 'metadata.annotations."thobits.com/git-sha"' ).find(yml) matches_good = json_path( 'spec.template.metadata.annotations."thobits.com/git-sha"' ).find(yml) if matches_bad: raise LintError(f'move git-sha annotation to ' f'spec.template.metadata.annotations for {ident}') if not matches_good: raise LintError(f'git-sha missing on {ident}')
def named_route_for(yml: object): """ require the existance of a route with matching parameters """ ident = object_ident(yml) for jp, value in json_path_to_value.items(): matches = json_path(jp).find(yml) if not all(m.value == value for m in matches): raise LintError(f"{jp}={value} not found") click.secho( f'* possible matching {ident} for "{name}" ' f'found({jp}={value})', fg='green')
def route_warn_public(yml: object): """Warn for every public route.""" matches = json_path( 'metadata.annotations."acme.openshift.io/exposer"').find(yml) if matches: click.secho(f'* public route found: {object_ident(yml)}', fg='yellow')