Compare commits

...
5 Commits
Author SHA1 Message Date
Nikolay Edigaryev 8554e56e4b Registry: use issued_at field in the token response message (#65)
* Registry: use issued_at field in the token response message

* Configure JSONDecoder instead of the Decodable itself
2022-05-12 22:33:02 +03:00
Nikolay Edigaryev 3fdcc5c33c .goreleaser(brews): depend on macOS Monterey (#64)
* .goreleaser(brews): depend on macOS Monterey

* README.md: mention that macOS Monterey or later is required
2022-05-12 00:42:12 +03:00
Austin Culter 182ddf0268 Minor typo: 'reposiotry' > 'repository' (#61)
On the tin.
2022-05-10 17:16:03 -04:00
Fedor Korotkov 24375c24f3 Print total size of compressed layers (#60)
Before pulling an image.
2022-05-10 16:20:52 -04:00
Fedor Korotkov 4ebec53c77 Two more FAQs (#58)
* Two more FAQs

* Better summary

* Another clarification
2022-05-10 23:13:17 +03:00
5 changed files with 93 additions and 11 deletions
+2
View File
@@ -35,3 +35,5 @@ brews:
homepage: https://github.com/cirruslabs/tart
description: Run macOS VMs on Apple Silicon
skip_upload: auto
custom_block: |
depends_on :macos => :monterey
+22 -2
View File
@@ -8,7 +8,7 @@ Built by CI engineers for your automation needs. Here are some highlights of Tar
* Use Tart Packer Plugin to automate VM creation.
* Built-in CI integration.
Try running a Tart VM on your Apple Silicon device (will download a 25 GB image):
Try running a Tart VM on your Apple Silicon device running macOS Monterey or later (will download a 25 GB image):
```shell
brew install cirruslabs/cli/tart
@@ -78,7 +78,7 @@ Please refer to `tart set --help` for additional details.
### Building with Packer
Please refer to [Tart Packer Plugin reposiotry](https://github.com/cirruslabs/packer-plugin-tart) for setup instructions.
Please refer to [Tart Packer Plugin repository](https://github.com/cirruslabs/packer-plugin-tart) for setup instructions.
Here is an example of a template to build `monterey-base` local image based of a remote image:
```json
@@ -160,3 +160,23 @@ tart pull acme.io/remoteorg/name:latest my-local-vm-name
Tart is a relatively small project, and it didn't feel right to try to monetize it.
Apple did all the heavy lifting with their `Virtualization.Framework`.
</details>
<details>
<summary>How to change VM's disk size?</summary>
You can choose disk size upon creation of a virtual machine:
```shell
tart create --from-ipsw=latest --disk-size=25 monterey-vanilla
```
For an existing VM please use [Packer Plugin](https://github.com/cirruslabs/packer-plugin-tart) which can increase
disk size for new virtual machines. Here is an example of [how to change disk size in a Packer template](https://github.com/cirruslabs/macos-image-templates/blob/fb0bcf68e0b093129136875c050205a66729b596/templates/base.pkr.hcl#L15).
</details>
<details>
<summary>Nested virtualization support?</summary>
Tart is limited by functionality of Apple's `Virtualization.Framework`. At the moment `Virtualization.Framework`
doesn't support nested virtualization.
</details>
+27 -7
View File
@@ -8,11 +8,31 @@ enum RegistryError: Error {
}
struct TokenResponse: Decodable {
let creationTime = Date()
let defaultIssuedAt = Date()
let defaultExpiresIn = 60
var token: String
var expires_in: Int?
var expiresIn: Int?
var issuedAt: Date?
static func parse(fromData: Data) throws -> Self {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
// RFC3339 date formatter from Apple's documentation[1]
//
// [1]: https://developer.apple.com/documentation/foundation/dateformatter
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
decoder.dateDecodingStrategy = .formatted(dateFormatter)
return try decoder.decode(TokenResponse.self, from: fromData)
}
var tokenExpiresAt: Date {
get {
// Tokens can expire and expire_in field is used to determine when:
@@ -22,8 +42,8 @@ struct TokenResponse: Decodable {
// >a token should never be returned with less than 60 seconds to live.
//
// [1]: https://docs.docker.com/registry/spec/auth/token/#requesting-a-token
creationTime + TimeInterval(expires_in ?? 60)
(issuedAt ?? defaultIssuedAt) + TimeInterval(expiresIn ?? defaultExpiresIn)
}
}
@@ -233,7 +253,7 @@ class Registry {
+ "while retrieving an authentication token", details: String(decoding: tokenResponseRaw, as: UTF8.self))
}
currentAuthToken = try JSONDecoder().decode(TokenResponse.self, from: tokenResponseRaw)
currentAuthToken = try TokenResponse.parse(fromData: tokenResponseRaw)
}
private func authAwareRequest(request: URLRequest) async throws -> (Data, HTTPURLResponse) {
+4 -2
View File
@@ -52,8 +52,10 @@ extension VMDirectory {
}
// Progress
defaultLogger.appendNewLine("pulling disk...")
let progress = Progress(totalUnitCount: Int64(diskLayers.map{ $0.size }.reduce(0) { $0 + $1 }))
let diskCompressedSize: Int64 = Int64(diskLayers.map {$0.size}.reduce(0) {$0 + $1})
let prettyDiskSize = String(format: "%.1f", Double(diskCompressedSize) / 1_000_000_000.0)
defaultLogger.appendNewLine("pulling disk (\(prettyDiskSize) GB compressed)...")
let progress = Progress(totalUnitCount: diskCompressedSize)
ProgressObserver(progress).log(defaultLogger)
for diskLayer in diskLayers {
+38
View File
@@ -0,0 +1,38 @@
import XCTest
@testable import tart
final class TokenResponseTests: XCTestCase {
func testBasic() throws {
let tokenResponseRaw = Data("{\"token\":\"some token\"}".utf8)
let tokenResponse = try TokenResponse.parse(fromData: tokenResponseRaw)
XCTAssertEqual(tokenResponse.token, "some token")
let expectedTokenExpiresAtRange = Date()...Date().addingTimeInterval(60)
XCTAssertTrue(expectedTokenExpiresAtRange.contains(tokenResponse.tokenExpiresAt))
XCTAssertTrue(tokenResponse.isValid)
}
func testExpirationBasic() throws {
let tokenResponseRaw = Data("{\"token\":\"some token\",\"expires_in\":2}".utf8)
let tokenResponse = try TokenResponse.parse(fromData: tokenResponseRaw)
XCTAssertEqual(tokenResponse.expiresIn, 2)
let expectedTokenExpiresAtRange = Date()...Date().addingTimeInterval(2)
XCTAssertTrue(expectedTokenExpiresAtRange.contains(tokenResponse.tokenExpiresAt))
XCTAssertTrue(tokenResponse.isValid)
_ = XCTWaiter.wait(for: [expectation(description: "Wait 3 seconds for the token to become invalid")], timeout: 2)
XCTAssertFalse(tokenResponse.isValid)
}
func testExpirationWithIssuedAt() throws {
let tokenResponseRaw = Data("{\"token\":\"some token\",\"expires_in\":3600,\"issued_at\":\"1970-01-01T00:00:00Z\"}".utf8)
let tokenResponse = try TokenResponse.parse(fromData: tokenResponseRaw)
XCTAssertEqual(Date(timeIntervalSince1970: 3600), tokenResponse.tokenExpiresAt)
XCTAssertFalse(tokenResponse.isValid)
}
}