Skip to content

Commit 2d2cbd4

Browse files
authored
Merge pull request #107 from hashicorp/bugfix/config-version-upload
fix(config-version): Updated upload method and ConfigurationSource Enum in the configuration version
2 parents 33996a2 + 2dce07d commit 2d2cbd4

4 files changed

Lines changed: 46 additions & 88 deletions

File tree

examples/configuration_version.py

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -277,29 +277,8 @@ def main():
277277
print(f" - {filename} ({size} bytes)")
278278

279279
try:
280-
# Create tar.gz archive manually since go-slug isn't available
281-
print("Creating tar.gz archive manually...")
282-
283-
import tarfile
284-
285-
# Create tar.gz archive in memory
286-
archive_buffer = io.BytesIO()
287-
with tarfile.open(fileobj=archive_buffer, mode="w:gz") as tar:
288-
# Add all files from the temp directory
289-
for filename in files:
290-
filepath = os.path.join(temp_dir, filename)
291-
tar.add(filepath, arcname=filename)
292-
293-
archive_buffer.seek(0)
294-
archive_bytes = archive_buffer.getvalue()
295-
print(f"Created archive: {len(archive_bytes)} bytes")
296-
297-
# Use the SDK's upload_tar_gzip method instead of direct HTTP calls
298-
print("Uploading archive using SDK method...")
299-
archive_buffer.seek(0) # Reset buffer position
300-
client.configuration_versions.upload_tar_gzip(
301-
new_cv.upload_url, archive_buffer
302-
)
280+
print("Uploading Terraform configuration...")
281+
client.configuration_versions.upload(new_cv.upload_url, temp_dir)
303282
print("Terraform configuration uploaded successfully!")
304283

305284
# Wait and check status
@@ -408,8 +387,6 @@ def main():
408387
# =====================================================
409388
# TEST 4: UPLOAD CONFIGURATION VERSION
410389
# =====================================================
411-
# Test 4: Upload function (requires go-slug)
412-
# =====================================================
413390
print("\n4. Testing upload() function:")
414391
try:
415392
# Create a fresh configuration version specifically for upload testing
@@ -441,33 +418,19 @@ def main():
441418
print(f"\n Uploading configuration to CV: {fresh_cv.id}")
442419
print(f"Upload URL: {upload_url[:60]}...")
443420

444-
try:
445-
client.configuration_versions.upload(upload_url, temp_dir)
446-
print("Configuration uploaded successfully!")
447-
448-
# Check status after upload
449-
print("\n Checking status after upload:")
450-
time.sleep(3) # Give TFE time to process
451-
updated_cv = client.configuration_versions.read(fresh_cv.id)
452-
print(f"Status after upload: {updated_cv.status}")
421+
client.configuration_versions.upload(upload_url, temp_dir)
422+
print("Configuration uploaded successfully!")
453423

454-
if updated_cv.status.value != "pending":
455-
print("Status changed (upload processed)")
456-
else:
457-
print("Status still pending (may need more time)")
424+
# Check status after upload
425+
print("\n Checking status after upload:")
426+
time.sleep(3) # Give TFE time to process
427+
updated_cv = client.configuration_versions.read(fresh_cv.id)
428+
print(f"Status after upload: {updated_cv.status}")
458429

459-
except ImportError as e:
460-
if "go-slug" in str(e):
461-
print("go-slug package not available")
462-
print("Install with: pip install go-slug")
463-
print(
464-
"Upload function exists but requires go-slug for packaging"
465-
)
466-
print(
467-
"Function correctly raises ImportError when go-slug unavailable"
468-
)
469-
else:
470-
raise
430+
if updated_cv.status.value != "pending":
431+
print("Status changed (upload processed)")
432+
else:
433+
print("Status still pending (may need more time)")
471434

472435
except Exception as e:
473436
print(f"Error: {e}")
@@ -868,7 +831,7 @@ def main():
868831
"TEST 2: create() - Create new configuration versions with different options"
869832
)
870833
print("TEST 3: read() - Read configuration version details and validate fields")
871-
print("TEST 4: upload() - Upload Terraform configurations (requires go-slug)")
834+
print("TEST 4: upload() - Upload Terraform configurations (stdlib tarfile)")
872835
print("TEST 5: download() - Download configuration version archives")
873836
print("TEST 6: archive() - Archive configuration versions")
874837
print("TEST 7: read_with_options() - Read with include options")

src/pytfe/models/configuration_version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ConfigurationSource(str, Enum):
2525
GITLAB = "gitlab"
2626
ADO = "ado"
2727
TERRAFORM = "terraform"
28+
TERRAFORM_CLOUD = "terraform+cloud"
2829

2930

3031
class ConfigVerIncludeOpt(str, Enum):

src/pytfe/utils.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
22

33
import io
4+
import os
45
import re
6+
import tarfile
57
import time
68
from collections.abc import Callable, Mapping
79
from typing import TYPE_CHECKING, Any
@@ -15,11 +17,6 @@
1517

1618
from urllib.parse import urlparse
1719

18-
try:
19-
import slug # type: ignore[import-not-found]
20-
except ImportError:
21-
slug = None
22-
2320
from .errors import (
2421
InvalidNameError,
2522
RequiredAgentModeError,
@@ -366,26 +363,25 @@ def pack_contents(path: str) -> io.BytesIO:
366363
BytesIO buffer containing the tar.gz archive
367364
368365
Raises:
369-
ImportError: If go-slug is not available
370366
ValueError: If path is invalid
371367
"""
372-
if slug is None:
373-
raise ImportError(
374-
"go-slug package is required for packing configuration files. "
375-
"Install it with: pip install go-slug"
368+
if not path or not os.path.isdir(path):
369+
raise ValueError(
370+
f"Failed to pack directory {path}: path must be an existing directory"
376371
)
377372

378373
body = io.BytesIO()
379374

380-
# Use go-slug to pack the configuration directory
381-
# This handles .terraformignore and other Terraform-specific behaviors
382-
packer = slug.Packer()
383-
_, err = packer.pack(path, body)
384-
385-
if err:
386-
raise ValueError(f"Failed to pack directory {path}: {err}")
375+
with tarfile.open(fileobj=body, mode="w:gz") as tar:
376+
for root, _, files in os.walk(path):
377+
rel_root = os.path.relpath(root, path)
378+
for filename in files:
379+
full_path = os.path.join(root, filename)
380+
arcname = (
381+
filename if rel_root == "." else os.path.join(rel_root, filename)
382+
)
383+
tar.add(full_path, arcname=arcname, recursive=False)
387384

388-
# Reset buffer position to beginning for reading
389385
body.seek(0)
390386
return body
391387

tests/units/test_configuration_version.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"""
1818

1919
import io
20-
from unittest.mock import Mock, patch
20+
from unittest.mock import Mock
2121

2222
import pytest
2323

@@ -343,35 +343,33 @@ def test_read_with_options_no_ingress(
343343
class TestConfigurationVersionsUpload:
344344
"""Test configuration versions upload functionality."""
345345

346-
def test_upload_missing_slug(self, configuration_versions_service):
347-
"""Test upload when go-slug is not available."""
346+
def test_upload_packs_with_tar(self, configuration_versions_service, tmp_path):
347+
"""Test upload works by packing a directory to tar.gz with stdlib."""
348348
upload_url = "https://example.com/upload"
349-
directory_path = "/tmp/test"
349+
directory_path = tmp_path
350+
(directory_path / "main.tf").write_text('resource "null_resource" "test" {}')
350351

351-
with patch("src.pytfe.utils.slug", None):
352-
with pytest.raises(ImportError, match="go-slug package is required"):
353-
configuration_versions_service.upload(upload_url, directory_path)
352+
mock_response = Mock()
353+
mock_response.status_code = 200
354+
configuration_versions_service.t._sync.put.return_value = mock_response
354355

355-
@patch("src.pytfe.utils.slug")
356-
def test_upload_success(self, mock_slug, configuration_versions_service):
357-
"""Test successful upload."""
358-
# Mock slug.pack
359-
mock_packer = Mock()
360-
mock_packer.pack.return_value = (None, None) # (size, error)
361-
mock_slug.Packer.return_value = mock_packer
356+
configuration_versions_service.upload(upload_url, str(directory_path))
362357

358+
configuration_versions_service.t._sync.put.assert_called_once()
359+
360+
def test_upload_success(self, configuration_versions_service, tmp_path):
361+
"""Test successful upload."""
363362
upload_url = "https://example.com/upload"
364-
directory_path = "/tmp/test"
363+
directory_path = tmp_path
364+
(directory_path / "main.tf").write_text('resource "null_resource" "test" {}')
365365

366366
# Mock transport's underlying httpx client instead of direct httpx
367367
mock_response = Mock()
368368
mock_response.status_code = 200
369369
configuration_versions_service.t._sync.put.return_value = mock_response
370370

371-
configuration_versions_service.upload(upload_url, directory_path)
372-
373-
# Verify slug.pack was called
374-
mock_packer.pack.assert_called_once()
371+
configuration_versions_service.upload(upload_url, str(directory_path))
372+
configuration_versions_service.t._sync.put.assert_called_once()
375373

376374

377375
class TestConfigurationVersionsUploadTarGzip:

0 commit comments

Comments
 (0)