|
| 1 | +# Fake Skopeo Client Implementation Summary |
| 2 | + |
| 3 | +## What Was Implemented |
| 4 | + |
| 5 | +A complete fake/mock implementation of `SkopeoClient` for testing purposes, based on the design discussion. |
| 6 | + |
| 7 | +## Files Created |
| 8 | + |
| 9 | +1. **`scripts/python/helpers/fake/__init__.py`** |
| 10 | + - Exports `FakeSkopeoClient` and `patch_skopeo_client()` |
| 11 | + - The `patch_skopeo_client()` function performs monkey-patching |
| 12 | + |
| 13 | +2. **`scripts/python/helpers/fake/skopeo.py`** |
| 14 | + - `FakeSkopeoClient` class - main implementation |
| 15 | + - Loads YAML config from `RELEASE_SERVICE_UTILS_FAKE_SKOPEO_SETUP` env var |
| 16 | + - Implements `inspect()` and `copy()` with full method signatures |
| 17 | + - Supports regex matching, first-match-wins rule evaluation |
| 18 | + - Validates config at load time |
| 19 | + - Raises `SkopeoClientError` when no match found |
| 20 | + |
| 21 | +3. **`scripts/python/helpers/fake/README.md`** |
| 22 | + - Comprehensive documentation |
| 23 | + - Usage examples |
| 24 | + - YAML format reference |
| 25 | + - Troubleshooting guide |
| 26 | + |
| 27 | +4. **`scripts/python/helpers/fake/example_config.yaml`** |
| 28 | + - Demonstrates all features: exact matching, regex, success/failure cases |
| 29 | + - Ready-to-use examples for common test scenarios |
| 30 | + |
| 31 | +5. **`scripts/python/helpers/fake/test_fake_skopeo.py`** |
| 32 | + - 13 unit tests covering all functionality |
| 33 | + - All tests passing |
| 34 | + - Validates load-time checks, runtime matching, error handling |
| 35 | + |
| 36 | +6. **`scripts/python/helpers/fake/example_test.sh`** |
| 37 | + - Working end-to-end example using bash wrapper |
| 38 | + - Demonstrates integration with `publish_index_image` |
| 39 | + - Shows how to use in Tekton/CI tests |
| 40 | + |
| 41 | +## Key Design Decisions |
| 42 | + |
| 43 | +### Activation Mechanism |
| 44 | +- **Environment variable**: `RELEASE_SERVICE_UTILS_FAKE_SKOPEO_SETUP` points to YAML config |
| 45 | +- **Monkey patching**: `patch_skopeo_client()` replaces real client before imports |
| 46 | +- **Drop-in replacement**: Same constructor and method signatures as `SkopeoClient` |
| 47 | + |
| 48 | +### YAML Structure |
| 49 | +- Top-level operations: `inspect`, `copy` |
| 50 | +- Each operation has list of rules with `match` and optional `return` |
| 51 | +- First matching rule wins (top-to-bottom evaluation) |
| 52 | + |
| 53 | +### Matching Logic |
| 54 | +- Only fields specified in `match` must match |
| 55 | +- Extra parameters in actual calls are ignored |
| 56 | +- Secrets are always ignored in matching |
| 57 | +- `None` values don't match specified patterns |
| 58 | +- Regex support via `{regex: "pattern"}` with fullmatch semantics |
| 59 | + |
| 60 | +### Validation |
| 61 | +- **Load-time validation**: YAML syntax, rule structure, return types |
| 62 | +- **Runtime validation**: Regex patterns applied during matching |
| 63 | +- **Error messages**: Detailed, showing attempted params and config file path |
| 64 | + |
| 65 | +### Return Value Handling |
| 66 | + |
| 67 | +**For `inspect()`:** |
| 68 | +- `format` specified in match → return must be string |
| 69 | +- No `format` in match → return must be dict |
| 70 | +- Auto-detected based on match rule |
| 71 | + |
| 72 | +**For `copy()`:** |
| 73 | +- Omit `return` section → success (returns `None`) |
| 74 | +- `return: {success: true}` → explicit success |
| 75 | +- `return: {success: false, ...}` → raises `SkopeoClientError` |
| 76 | +- Default values: `returncode=1`, `stdout=""`, `stderr=""` |
| 77 | + |
| 78 | +## Usage Pattern for Tests |
| 79 | + |
| 80 | +```bash |
| 81 | +# 1. Create mock config |
| 82 | +cat > mock-config.yaml <<EOF |
| 83 | +inspect: |
| 84 | + - match: |
| 85 | + image: "docker://quay.io/target:tag" |
| 86 | + format: "{{.Digest}}" |
| 87 | + return: "sha256:different" |
| 88 | +
|
| 89 | +copy: |
| 90 | + - match: |
| 91 | + source: "docker://quay.io/source@sha256:abc" |
| 92 | + destination: "docker://quay.io/target:tag" |
| 93 | +EOF |
| 94 | + |
| 95 | +# 2. Set environment variable |
| 96 | +export RELEASE_SERVICE_UTILS_FAKE_SKOPEO_SETUP=/path/to/mock-config.yaml |
| 97 | + |
| 98 | +# 3. Create bash wrapper |
| 99 | +publish_index_image() { |
| 100 | + python3 - "$@" <<'PYTHON' |
| 101 | +import sys |
| 102 | +sys.path.insert(0, '/path/to/helpers') |
| 103 | +sys.path.insert(0, '/path/to/tasks/internal') |
| 104 | +
|
| 105 | +from fake import patch_skopeo_client |
| 106 | +patch_skopeo_client() |
| 107 | +
|
| 108 | +from publish_index_image import main |
| 109 | +sys.exit(main()) |
| 110 | +PYTHON |
| 111 | +} |
| 112 | + |
| 113 | +# 4. Run your test |
| 114 | +publish_index_image --source-index "..." --target-index "..." |
| 115 | +``` |
| 116 | + |
| 117 | +## Testing the Implementation |
| 118 | + |
| 119 | +```bash |
| 120 | +# Run unit tests |
| 121 | +pytest scripts/python/helpers/fake/test_fake_skopeo.py -v |
| 122 | + |
| 123 | +# Run integration example |
| 124 | +./scripts/python/helpers/fake/example_test.sh |
| 125 | +``` |
| 126 | + |
| 127 | +## What's NOT Included |
| 128 | + |
| 129 | +Based on our design discussion, the following were explicitly excluded: |
| 130 | + |
| 131 | +- Multiple config file support (only single file) |
| 132 | +- Special debug/strict/recording modes |
| 133 | +- Validation of dict field names (type-only validation) |
| 134 | +- Matching on constructor parameters |
| 135 | +- Matching on credential values |
| 136 | +- Template parsing for `format` parameter (returns pre-formatted strings) |
| 137 | + |
| 138 | +These can be added later if needed without breaking existing configs. |
| 139 | + |
| 140 | +## Next Steps |
| 141 | + |
| 142 | +1. **In your other project**: Create test YAML configs specific to your test scenarios |
| 143 | +2. **Update CI/Tekton tasks**: Use the bash wrapper pattern to inject fake client |
| 144 | +3. **Write tests**: Use `example_test.sh` as a template for your specific test cases |
| 145 | +4. **Iterate**: Add more rules to your YAML configs as you encounter new test scenarios |
| 146 | + |
0 commit comments