Skip to content

Commit

Permalink
Merge pull request #42 from CMInformatik/feat/add_resource_options
Browse files Browse the repository at this point in the history
Feat/add resource options
  • Loading branch information
Noahnc authored Jan 3, 2024
2 parents 0998bff + af803e6 commit 323679a
Show file tree
Hide file tree
Showing 21 changed files with 491 additions and 184 deletions.
7 changes: 5 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff"
},
Expand Down Expand Up @@ -53,5 +53,8 @@
"python.analysis.autoSearchPaths": false,
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[markdown]": {
"editor.defaultFormatter": "yzhang.markdown-all-in-one"
}
}
67 changes: 63 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ The CLI works by scanning your .tf files for versioned providers and modules and
- [Authentication](#authentication-1)
- [.terraformrc file:](#terraformrc-file)
- [infrapatch\_credentials.json file:](#infrapatch_credentialsjson-file)
- [Setup Development Environment for InfraPatch](#setup-development-environment-for-infrapatch)
- [Contributing](#contributing)
- [Global](#global)
- [Resource Options](#resource-options)
- [Available Options](#available-options)
- [Example](#example)
- [Setup Development Environment for InfraPatch](#setup-development-environment-for-infrapatch)
- [Contributing](#contributing)


## GitHub Action
Expand Down Expand Up @@ -184,7 +188,62 @@ You can also specify the path to the credentials file with the `--credentials-fi
infrapatch --credentials-file-path "path/to/credentials/file" update
```

### Setup Development Environment for InfraPatch
## Global

The following section describes configurations and behaviors that are applicable to the Github Action and the CLI.

### Resource Options

InfraPatch supports individual resource options to change the behavior for a specific resource.
Resource options can be specified one line obove your resource definition with the following syntax:

```hcl
# infrapatch_options: <option_name1>=<option_value1>, <option_name2>=<option_value2>
module "example" {
source = "terraform-aws-modules/example"
name: "demo"
}
terraform {
required_providers {
# infrapatch_options: <option_name1>=<option_value1>,<option_name2>=<option_value2>
aws = {
source = "hashicorp/aws"
}
}
}
```

#### Available Options

Currently, the following options are available:

| Option Name | Description | Default Value |
| ----------------- | ------------------------------------------------------------- | ------------- |
| `ignore_resource` | If set to `true`, the resource will be ignored by InfraPatch. | `false` |

#### Example

The following example shows how to ignore a terraform module and a terraform provider:

```hcl
# infrapatch_options: ignore_resource=true
module "example" {
source = "terraform-aws-modules/example"
name: "demo"
}
terraform {
required_providers {
# infrapatch_options: ignore_resource=true
aws = {
source = "hashicorp/aws"
}
}
}
```

## Setup Development Environment for InfraPatch

This repository contains a devcontainer configuration for VSCode. To use it, you need to install the following tools:
* ["Dev Containers VSCode Extension"](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) for VSCode.
Expand All @@ -193,7 +252,7 @@ This repository contains a devcontainer configuration for VSCode. To use it, you
After installation, you can open the repository in the devcontainer by clicking on the green "Open in Container" button in the bottom left corner of VSCode.
During the first start, the devcontainer will build the container image and install all dependencies.

### Contributing
## Contributing

If you have any ideas for improvements or find any bugs, feel free to open an issue or create a pull request.

2 changes: 1 addition & 1 deletion infrapatch/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.5.0"
__version__ = "0.6.0"
2 changes: 2 additions & 0 deletions infrapatch/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
APP_NAME = "InfraPatch"

DEFAULT_CREDENTIALS_FILE_NAME = "infrapatch_credentials.json"

infrapatch_options_prefix = "# infrapatch_options:"
10 changes: 3 additions & 7 deletions infrapatch/core/models/statistics.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
from dataclasses import dataclass
import dataclasses
from typing import Any, Sequence
from pydantic import BaseModel
from pytablewriter import MarkdownTableWriter
from rich.table import Table
from infrapatch.core.models.versioned_resource import VersionedResource


@dataclass
class BaseStatistics:
class BaseStatistics(BaseModel):
errors: int
resources_patched: int
resources_pending_update: int
total_resources: int

def to_dict(self) -> dict[str, Any]:
return dataclasses.asdict(self)
return self.model_dump()


@dataclass
class ProviderStatistics(BaseStatistics):
resources: Sequence[VersionedResource]


@dataclass
class Statistics(BaseStatistics):
providers: dict[str, ProviderStatistics]

Expand Down
62 changes: 33 additions & 29 deletions infrapatch/core/models/tests/test_versioned_resource.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pathlib import Path
from pathlib import Path, PosixPath

import pytest

Expand All @@ -7,7 +7,7 @@

def test_version_management():
# Create new resource with newer version
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = "2.0.0"

assert resource.status == ResourceStatus.UNPATCHED
Expand All @@ -17,103 +17,107 @@ def test_version_management():
assert resource.status == ResourceStatus.PATCHED

# Check new_version the same as current_version
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = "1.0.0"

assert resource.status == ResourceStatus.UP_TO_DATE
assert resource.installed_version_equal_or_newer_than_new_version() is True

# Check new_version older than current_version
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = "0.1.0"

assert resource.status == ResourceStatus.UP_TO_DATE
assert resource.installed_version_equal_or_newer_than_new_version() is True


def test_tile_constraint():
resource = VersionedResource(name="test_resource", current_version="~>1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="~>1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = "~>1.0.1"
assert resource.has_tile_constraint() is True
assert resource.installed_version_equal_or_newer_than_new_version() is True

resource.newest_version = "~>1.1.0"
assert resource.installed_version_equal_or_newer_than_new_version() is False

resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
assert resource.has_tile_constraint() is False

resource = VersionedResource(name="test_resource", current_version="~>1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="~>1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = "1.1.0"
assert resource.newest_version == "~>1.1.0"


def test_git_repo():
resource = VersionedResource(name="test_resource", current_version="~>1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="~>1.0.0", source_file=Path("test_file.py"), start_line_number=1)

assert resource.github_repo is None

resource.set_github_repo("https://github.com/noahnc/test_repo.git")
resource.github_repo = "https://github.com/noahnc/test_repo.git"
assert resource.github_repo == "noahnc/test_repo"

resource.set_github_repo("https://github.com/noahnc/test_repo")
resource.github_repo = "https://github.com/noahnc/test_repo"
assert resource.github_repo == "noahnc/test_repo"

with pytest.raises(Exception):
resource.set_github_repo("https://github.com/")
resource.github_repo = "https://github.com/"

with pytest.raises(Exception):
resource.set_github_repo("https://github.com")
resource.github_repo = "https://github.com"


def test_patch_error():
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.set_patch_error()
assert resource.status == ResourceStatus.PATCH_ERROR


def test_version_not_found():
# Test manual setting
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.set_no_version_found()
assert resource.status == ResourceStatus.NO_VERSION_FOUND
assert resource.installed_version_equal_or_newer_than_new_version() is True

# Test by setting None as new version
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
resource.newest_version = None
assert resource.status == ResourceStatus.NO_VERSION_FOUND
assert resource.installed_version_equal_or_newer_than_new_version() is True


def test_path():
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="/var/testdir/test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("/var/testdir/test_file.py"), start_line_number=1)
assert resource.source_file == Path("/var/testdir/test_file.py")


def test_find():
findably_resource = VersionedResource(name="test_resource3", current_version="1.0.0", _source_file="test_file3.py")
unfindably_resource = VersionedResource(name="test_resource6", current_version="1.0.0", _source_file="test_file8.py")
findably_resource = VersionedResource(name="test_resource3", current_version="1.0.0", source_file=Path("test_file3.py"), start_line_number=1)
unfindably_resource = VersionedResource(name="test_resource6", current_version="1.0.0", source_file=Path("test_file8.py"), start_line_number=1)
resources = [
VersionedResource(name="test_resource1", current_version="1.0.0", _source_file="test_file1.py"),
VersionedResource(name="test_resource2", current_version="1.0.0", _source_file="test_file2.py"),
VersionedResource(name="test_resource3", current_version="1.0.0", _source_file="test_file3.py"),
VersionedResource(name="test_resource4", current_version="1.0.0", _source_file="test_file4.py"),
VersionedResource(name="test_resource5", current_version="1.0.0", _source_file="test_file5.py"),
VersionedResource(name="test_resource1", current_version="1.0.0", source_file=Path("test_file1.py"), start_line_number=1),
VersionedResource(name="test_resource2", current_version="1.0.0", source_file=Path("test_file2.py"), start_line_number=1),
VersionedResource(name="test_resource3", current_version="1.0.0", source_file=Path("test_file3.py"), start_line_number=1),
VersionedResource(name="test_resource4", current_version="1.0.0", source_file=Path("test_file4.py"), start_line_number=1),
VersionedResource(name="test_resource5", current_version="1.0.0", source_file=Path("test_file5.py"), start_line_number=1),
]
assert len(findably_resource.find(resources)) == 1
assert findably_resource.find(resources) == [resources[2]]
assert len(unfindably_resource.find(resources)) == 0


def test_versioned_resource_to_dict():
resource = VersionedResource(name="test_resource", current_version="1.0.0", _source_file="test_file.py")
resource = VersionedResource(name="test_resource", current_version="1.0.0", source_file=Path("test_file.py"), start_line_number=1)
expected_dict = {
"name": "test_resource",
"current_version": "1.0.0",
"_source_file": "test_file.py",
"_newest_version": None,
"_status": ResourceStatus.UNPATCHED,
"_github_repo": None,
"source_file": PosixPath("test_file.py"),
"newest_version_string": None,
"status": ResourceStatus.UNPATCHED,
"github_repo_string": None,
"start_line_number": 1,
"options": {
"ignore_resource": False,
},
}
assert resource.to_dict() == expected_dict
assert resource.model_dump() == expected_dict
Loading

0 comments on commit 323679a

Please sign in to comment.