Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 67 additions & 0 deletions contrib/terraform/fixtures/codeartifact/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
resource "aws_kms_key" "artifacts" {
description = "cloudemu-tf-codeartifact"
deletion_window_in_days = 7
}

resource "aws_codeartifact_domain" "main" {
domain = "cloudemu-tf"
encryption_key = aws_kms_key.artifacts.arn

tags = {
env = "test"
}
}

# One repository holds the external connection; the other consumes it as an upstream.
resource "aws_codeartifact_repository" "npm_store" {
repository = "npm-store"
domain = aws_codeartifact_domain.main.domain
description = "proxy of the public npm registry"

external_connections {
external_connection_name = "public:npmjs"
}
}

resource "aws_codeartifact_repository" "app" {
repository = "app"
domain = aws_codeartifact_domain.main.domain
description = "application packages"

upstream {
repository_name = aws_codeartifact_repository.npm_store.repository
}

tags = {
team = "platform"
}
}

resource "aws_codeartifact_domain_permissions_policy" "main" {
domain = aws_codeartifact_domain.main.domain

policy_document = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::123456789012:root" }
Action = ["codeartifact:CreateRepository", "codeartifact:DescribeDomain", "codeartifact:GetAuthorizationToken"]
Resource = "*"
}]
})
}

resource "aws_codeartifact_repository_permissions_policy" "app" {
repository = aws_codeartifact_repository.app.repository
domain = aws_codeartifact_domain.main.domain

policy_document = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::123456789012:root" }
Action = ["codeartifact:ReadFromRepository", "codeartifact:DescribePackageVersion"]
Resource = "*"
}]
})
}
29 changes: 29 additions & 0 deletions contrib/terraform/fixtures/codeartifact/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

variable "endpoint" {
type = string
description = "CloudEmu AWS endpoint URL"
}

provider "aws" {
access_key = "test"
secret_key = "test"
region = "us-east-1"

skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true

endpoints {
codeartifact = var.endpoint
kms = var.endpoint
sts = var.endpoint
}
}
2 changes: 1 addition & 1 deletion contrib/terraform/terraform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
func TestTerraformApplyIsIdempotent(t *testing.T) {
bin := tfBinary(t)

for _, fixture := range []string{"basic", "networking", "core-services", "cloudwatch-alarms", "cloudwatch-logs"} {
for _, fixture := range []string{"basic", "networking", "core-services", "cloudwatch-alarms", "cloudwatch-logs", "codeartifact"} {
t.Run(fixture, func(t *testing.T) {
applyFixture(t, fixture, bin)
})
Expand Down
96 changes: 95 additions & 1 deletion docs/coverage/aws/codeartifact.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,100 @@ AWS's `codeartifact` service · portable interface `driver.CodeArtifact` · [AWS
| `UntagResource` | |
| `UpdateRepository` | |

## Optional capabilities

Discovered by type assertion; only some providers implement these.

### Access

Access is the optional capability behind `aws codeartifact login`.

| Operation | Description |
| --- | --- |
| `GetAuthorizationToken` | GetAuthorizationToken issues a bearer token for a domain; a zero durationSeconds means the default lifetime. |
| `GetRepositoryEndpoint` | GetRepositoryEndpoint returns the URL of a repository for one package format ("", "ipv4" or "dualstack" endpoint type). |

### PackageGroups

PackageGroups is the optional capability for package groups.

| Operation | Description |
| --- | --- |
| `CreatePackageGroup` | CreatePackageGroup creates a package group. |
| `DeletePackageGroup` | DeletePackageGroup deletes a group; its children move up to its parent. |
| `DescribePackageGroup` | DescribePackageGroup returns a package group with its parent and effective restrictions. |
| `GetAssociatedPackageGroup` | GetAssociatedPackageGroup returns the group a package coordinate belongs to and how strongly. |
| `ListAllowedRepositoriesForGroup` | ListAllowedRepositoriesForGroup lists the allowed repositories of one restriction type. |
| `ListAssociatedPackages` | ListAssociatedPackages lists the packages whose most specific group is the given one. |
| `ListPackageGroups` | ListPackageGroups lists a domain's package groups. |
| `ListSubPackageGroups` | ListSubPackageGroups lists the direct children of a package group. |
| `UpdatePackageGroup` | UpdatePackageGroup changes the contact info and description of a group. |
| `UpdatePackageGroupOriginConfiguration` | UpdatePackageGroupOriginConfiguration sets restriction modes and edits the allowed repository lists. |

### PackageVersions

PackageVersions is the optional capability for package versions and their assets.

| Operation | Description |
| --- | --- |
| `CopyPackageVersions` | CopyPackageVersions copies versions between two repositories of one domain. |
| `DeletePackageVersions` | DeletePackageVersions removes versions and their assets. |
| `DescribePackageVersion` | DescribePackageVersion returns the description of one package version. |
| `DisposePackageVersions` | DisposePackageVersions deletes the assets of versions and marks them Disposed. |
| `GetPackageVersionAsset` | GetPackageVersionAsset returns the bytes of one asset. |
| `GetPackageVersionReadme` | GetPackageVersionReadme returns the identity of a version; generic versions have no readme text. |
| `ListPackageVersionAssets` | ListPackageVersionAssets lists the assets of a package version. |
| `ListPackageVersionDependencies` | ListPackageVersionDependencies returns the identity of a version; generic versions have no dependencies. |
| `ListPackageVersions` | ListPackageVersions lists the versions of a package, newest published first. |
| `PublishPackageVersion` | PublishPackageVersion adds an asset to a generic package version, creating the version when needed. |
| `UpdatePackageVersionsStatus` | UpdatePackageVersionsStatus moves versions to Archived, Published or Unlisted. |

### Packages

Packages is the optional capability for package records and their origin controls.

| Operation | Description |
| --- | --- |
| `DeletePackage` | DeletePackage deletes a package with all its versions and assets. |
| `DescribePackage` | DescribePackage returns a package record. |
| `ListPackages` | ListPackages lists the packages of a repository. |
| `PutPackageOriginConfiguration` | PutPackageOriginConfiguration sets a package's origin controls, creating an empty record when it does not exist. |

### Policies

Policies is the optional capability for domain and repository permissions policies.

| Operation | Description |
| --- | --- |
| `DeleteDomainPermissionsPolicy` | DeleteDomainPermissionsPolicy removes the resource policy from a domain. |
| `DeleteRepositoryPermissionsPolicy` | DeleteRepositoryPermissionsPolicy removes the resource policy from a repository. |
| `GetDomainPermissionsPolicy` | GetDomainPermissionsPolicy returns the resource policy on a domain. |
| `GetRepositoryPermissionsPolicy` | GetRepositoryPermissionsPolicy returns the resource policy on a repository. |
| `PutDomainPermissionsPolicy` | PutDomainPermissionsPolicy sets the resource policy on a domain. |
| `PutRepositoryPermissionsPolicy` | PutRepositoryPermissionsPolicy sets the resource policy on a repository. |

### RepositoryAdministratorLister

RepositoryAdministratorLister is the optional capability behind the administrator-account filter of

| Operation | Description |
| --- | --- |
| `ListRepositoriesInDomainByAdministrator` | ListRepositoriesInDomainByAdministrator lists a domain's repositories administered by one account. |

## Not in scope

_Not documented yet. See the [emulator boundary](../../../README.md) for cloudemu-wide non-goals._
- All 48 operations of the API (`aws-sdk-go-v2/service/codeartifact` v1.45.0) are served. What is not emulated is the package-manager side: there is no npm, pip, Maven, NuGet, RubyGems, Cargo, Swift or generic registry protocol. `GetRepositoryEndpoint` and `aws codeartifact login` work and report the URL AWS reports (`https://<domain>-<owner>.d.codeartifact.<region>.amazonaws.com/<format>/<repository>/`), but nothing serves that host, so `npm install` and `pip install` against it do not reach the emulator. The `dualstack` and `ipv4` endpoint types return the same host.
- Only generic packages can be created through the API, which is also true of real AWS (`PublishPackageVersion` is generic-only). `PutPackageOriginConfiguration` creates an empty package record of any format. Packages of other formats never appear on their own.
- An external connection is stored (its name, the package format it serves, status `Available`) but nothing is fetched from the public registry, so no version has the `EXTERNAL` origin type and `ListPackageVersions` with `originType` `EXTERNAL` or `UNKNOWN` is always empty. Retaining versions from upstream repositories happens only through `CopyPackageVersions` with `includeFromUpstream`.
- Generic package versions carry no metadata the emulator can read, so `GetPackageVersionReadme` returns an empty readme, `ListPackageVersionDependencies` an empty list, and `DescribePackageVersion` has no `summary`, `homePage`, `sourceCodeRepository` or `licenses`.
- Asset bytes are kept in memory. The limit per asset is 128 MiB (AWS: 5 GiB, adjustable) and a package version holds at most 350 assets, as in AWS; a larger asset answers `ServiceQuotaExceededException`. Assets are stored once per domain, so `assetSizeBytes` sums the distinct SHA-256 digests in the domain. A snapshot taken without assets keeps the name, size and hashes of every asset but not its bytes: downloading one afterwards answers `ResourceNotFoundException`. `--persist` always keeps the bytes.
- The request-rate quotas (publish 10/s, copy 5/s, token 40/s and the rest) and `ThrottlingException` are not modelled. `AccessDeniedException` comes only from `--enforce-auth` identity policies: domain and repository permissions policies are validated (a JSON document with a `Statement`, at most 7168 bytes) and stored with an optimistic-lock revision, but never evaluated, and cross-account access is not modelled. A domain and its repositories always belong to the emulator's single account, so the domain owner and the repository administrator are the same account and a package version event is delivered once.
- The `encryptionKey` of a domain is stored and returned but not checked against KMS and not used: assets are not encrypted. The `s3BucketArn` is a synthesized name; no bucket exists.
- Authorization tokens are opaque random strings valid for the requested duration (0 means the 12-hour default; AWS ties 0 to the caller's role session). Nothing consumes them: no registry checks a token.
- Only the `CodeArtifact Package Version State Change` EventBridge event is emitted: on publish, adding an asset, a status change, copy, dispose, delete versions and delete package (one event per version), with the documented detail and a per-package `sequenceNumber`. The issue asks for a repository state-change event, but AWS documents no payload for it (its EventBridge reference names it "Goshawk Repository State Change" and its CodeArtifact guide lists only the package version event), so none is emitted. Deleting a repository removes its packages without per-version events (a domain cannot be deleted while it has repositories). Ingestion events do not exist because nothing is ingested.
- The AWS reference does not state these behaviours; they were chosen conservatively and are unconfirmed against the live service: publishing a package that its package or group origin controls block, or a version that already exists in an upstream, answers `ConflictException`; a SHA-256 header that does not match the content answers `ValidationException`; adding an asset to a Published version answers `ConflictException`; downloading from an Archived version answers `ConflictException`; a name that is not one of the ten supported external connections, and an upstream that does not exist, answer `ResourceNotFoundException`; a repository that is another repository's upstream cannot be deleted (`ConflictException`); a repository holds upstreams or an external connection, not both; a package whose last version is deleted disappears unless its origin controls were set explicitly; a status update does not change a version's revision; package group errors carry a `resourceId` but no `resourceType`, because the API's `ResourceType` values do not include a package group; `DeleteDomain` answers `ValidationException` for a domain that does not exist, because the API does not model `ResourceNotFoundException` for it; a new domain has no default `/*` package group.
- A version's `revision` is a 32-character digest of its assets: it changes when an asset is added, removed or replaced, and stays the same across reads and status changes. A copy keeps the source revision.
- Package group patterns follow the grammar and word-boundary matching in the user guide. Matching a package to a group is exact (strong) or case, dash, dot and underscore tolerant (weak); the confusable-character normalization is not performed, and Python, NuGet and Swift names are not normalized before matching. A new group's three restrictions start as `INHERIT`, which resolves to `ALLOW` when no parent sets one. A weak match blocks publishing. Deleting a group re-parents its children and deleting a repository drops it from the allowed repository lists.
- Package origin controls default to publish `ALLOW` and upstream `BLOCK` for a package whose first version is published directly, or `ALLOW`/`ALLOW` when its group restricts anything. Origin controls limit publishing; upstream restrictions are stored and listed but only block a publish through the upstream-presence rule, because no ingestion exists.
- List operations take `maxResults` (1 to 1000, default 100) and `nextToken`, an opaque offset over results in a fixed order (names, or newest-published first for versions), so it is only stable while the list does not change between pages. A token the emulator did not issue is a `ValidationException`.
- `ListAssociatedPackages` and package group matching only see packages that exist in a repository (plus the packages `preview` evaluates for a group that does not exist yet); `GetAssociatedPackageGroup` works for coordinates that exist nowhere.
Loading
Loading