Skip to content
Merged
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
23 changes: 23 additions & 0 deletions src/Models/Certificates/Certificate.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

namespace LKDev\HetznerCloud\Models\Certificates;

use LKDev\HetznerCloud\APIResponse;
use LKDev\HetznerCloud\HetznerAPIClient;
use LKDev\HetznerCloud\Models\Actions\Action;
use LKDev\HetznerCloud\Models\Contracts\Resource;
use LKDev\HetznerCloud\Models\Model;

Expand Down Expand Up @@ -145,6 +147,27 @@ public static function parse($input)
return new self($input->id, $input->name, $input->certificate, $input->created, $input->not_valid_before, $input->not_valid_after, $input->domain_names, $input->fingerprint, $input->used_by, $input->labels, $input->type ?? null);
}

/**
* Retry a failed Certificate issuance or renewal (only for managed certificates).
*
* @see https://docs.hetzner.cloud/#certificate-actions-retry-issuance-or-renewal
*
* @return APIResponse|null
*
* @throws \LKDev\HetznerCloud\APIException
*/
public function retry(): ?APIResponse
{
$response = $this->httpClient->post('certificates/'.$this->id.'/actions/retry', []);
if (! HetznerAPIClient::hasError($response)) {
return APIResponse::create([
'action' => Action::parse(json_decode((string) $response->getBody())->action),
], $response->getHeaders());
}

return null;
}

/**
* Reload the data of the SSH Key.
*
Expand Down
30 changes: 30 additions & 0 deletions src/Models/LoadBalancers/LoadBalancer.php
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,36 @@ public function removeTarget(string $type, ?LoadBalancerTargetIp $ip = null, ?ar
return null;
}

/**
* Get Metrics for a Load Balancer.
*
* @see https://docs.hetzner.cloud/#load-balancers-get-metrics-for-a-load-balancer
*
* @param string $type Comma-separated list of metric types (open_connections, connections_per_second, requests_per_second, bandwidth)
* @param string $start Start of period (ISO 8601 date-time)
* @param string $end End of period (ISO 8601 date-time)
* @param int|null $step Resolution of results in seconds
* @return APIResponse|null
*
* @throws APIException
* @throws GuzzleException
*/
public function metrics(string $type, string $start, string $end, ?int $step = null): ?APIResponse
{
$params = compact('type', 'start', 'end');
if ($step !== null) {
$params['step'] = $step;
}
$response = $this->httpClient->get($this->replaceServerIdInUri('load_balancers/{id}/metrics?').http_build_query($params));
if (! HetznerAPIClient::hasError($response)) {
return APIResponse::create([
'metrics' => json_decode((string) $response->getBody())->metrics,
], $response->getHeaders());
}

return null;
}

/**
* Updates a Load Balancer Service.
*
Expand Down
64 changes: 64 additions & 0 deletions src/Models/LoadBalancers/LoadBalancers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use LKDev\HetznerCloud\APIResponse;
use LKDev\HetznerCloud\HetznerAPIClient;
use LKDev\HetznerCloud\Models\Actions\Action;
use LKDev\HetznerCloud\Models\Contracts\Resources;
use LKDev\HetznerCloud\Models\Meta;
use LKDev\HetznerCloud\Models\Model;
Expand Down Expand Up @@ -66,6 +67,69 @@ public function list(?RequestOpts $requestOpts = null): ?APIResponse
return null;
}

/**
* Creates a Load Balancer.
*
* @see https://docs.hetzner.cloud/#load-balancers-create-a-load-balancer
*
* @param string $name
* @param string $loadBalancerType ID or name of the Load Balancer type
* @param string|null $location ID or name of location (mutually exclusive with $networkZone)
* @param string|null $networkZone Name of network zone (mutually exclusive with $location)
* @param array|null $algorithm
* @param array $labels
* @param int|null $network
* @param bool $publicInterface
* @param array $services
* @param array $targets
* @return APIResponse|null
*
* @throws \LKDev\HetznerCloud\APIException
*/
public function create(string $name, string $loadBalancerType, ?string $location = null, ?string $networkZone = null, ?array $algorithm = null, array $labels = [], ?int $network = null, bool $publicInterface = true, array $services = [], array $targets = []): ?APIResponse
{
$payload = [
'name' => $name,
'load_balancer_type' => $loadBalancerType,
];
if ($location !== null) {
$payload['location'] = $location;
}
if ($networkZone !== null) {
$payload['network_zone'] = $networkZone;
}
if ($algorithm !== null) {
$payload['algorithm'] = $algorithm;
}
if (! empty($labels)) {
$payload['labels'] = $labels;
}
if ($network !== null) {
$payload['network'] = $network;
}
if (! $publicInterface) {
$payload['public_interface'] = false;
}
if (! empty($services)) {
$payload['services'] = $services;
}
if (! empty($targets)) {
$payload['targets'] = $targets;
}

$response = $this->httpClient->post('load_balancers', ['json' => $payload]);
if (! HetznerAPIClient::hasError($response)) {
$body = json_decode((string) $response->getBody());

return APIResponse::create([
'load_balancer' => LoadBalancer::parse($body->load_balancer),
'action' => Action::parse($body->action),
], $response->getHeaders());
}

return null;
}

/**
* Gets a specific Load Balancer object.
*
Expand Down
45 changes: 45 additions & 0 deletions src/Models/Servers/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,51 @@ public function changeAliasIPs(Network $network, array $aliasIps)
return null;
}

/**
* Adds a Server to a Placement Group.
*
* @see https://docs.hetzner.cloud/#server-actions-add-a-server-to-a-placement-group
*
* @param int $placementGroupId
* @return APIResponse|null
*
* @throws \LKDev\HetznerCloud\APIException
*/
public function addToPlacementGroup(int $placementGroupId): ?APIResponse
{
$response = $this->httpClient->post($this->replaceServerIdInUri('servers/{id}/actions/add_to_placement_group'), [
'json' => ['placement_group' => $placementGroupId],
]);
if (! HetznerAPIClient::hasError($response)) {
return APIResponse::create([
'action' => Action::parse(json_decode((string) $response->getBody())->action),
], $response->getHeaders());
}

return null;
}

/**
* Removes a Server from a Placement Group.
*
* @see https://docs.hetzner.cloud/#server-actions-remove-a-server-from-a-placement-group
*
* @return APIResponse|null
*
* @throws \LKDev\HetznerCloud\APIException
*/
public function removeFromPlacementGroup(): ?APIResponse
{
$response = $this->httpClient->post($this->replaceServerIdInUri('servers/{id}/actions/remove_from_placement_group'), []);
if (! HetznerAPIClient::hasError($response)) {
return APIResponse::create([
'action' => Action::parse(json_decode((string) $response->getBody())->action),
], $response->getHeaders());
}

return null;
}

/**
* @param string $uri
* @return string
Expand Down
26 changes: 26 additions & 0 deletions src/Models/Zones/RRSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,30 @@ public function removeRecords(array $records)

return null;
}

/**
* Update specific records in this RRSet.
*
* @see https://docs.hetzner.cloud/#zone-rrset-actions-update-records-in-a-rrset
*
* @param array<array{value: string, comment: string}> $records
* @return APIResponse|null
*
* @throws \LKDev\HetznerCloud\APIException
*/
public function updateRecords(array $records): ?APIResponse
{
$response = $this->httpClient->post('zones/'.$this->zone.'/rrsets/'.$this->id.'/actions/update_records', [
'json' => [
'records' => $records,
],
]);
if (! HetznerAPIClient::hasError($response)) {
return APIResponse::create([
'action' => Action::parse(json_decode((string) $response->getBody())->action),
], $response->getHeaders());
}

return null;
}
}
15 changes: 15 additions & 0 deletions tests/Unit/Models/Certificates/CertificatesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,19 @@ public function testDelete()
$this->assertTrue($certificate->delete());
$this->assertLastRequestEquals('DELETE', '/certificates/897');
}

public function testRetry()
{
$this->mockHandler->append(new Response(200, [], file_get_contents(__DIR__.'/fixtures/certificate.json')));
$certificate = $this->certificates->get(897);

$this->mockHandler->append(new Response(201, [], file_get_contents(__DIR__.'/fixtures/certificate_action_retry.json')));
$apiResponse = $certificate->retry();

$this->assertEquals('retry_issuance_or_renewal', $apiResponse->action->command);
$this->assertEquals(897, $apiResponse->action->resources[0]->id);
$this->assertEquals('certificate', $apiResponse->action->resources[0]->type);

$this->assertLastRequestEquals('POST', '/certificates/897/actions/retry');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"action": {
"command": "retry_issuance_or_renewal",
"error": {
"code": "action_failed",
"message": "Action failed"
},
"finished": "2016-01-30T23:56:00+00:00",
"id": 15,
"progress": 100,
"resources": [
{
"id": 897,
"type": "certificate"
}
],
"started": "2016-01-30T23:55:00+00:00",
"status": "success"
}
}
10 changes: 10 additions & 0 deletions tests/Unit/Models/LoadBalancers/LoadBalancerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,14 @@ public function testUpdateService()
'protocol' => 'https',
]);
}

public function testMetrics()
{
$this->mockHandler->append(new Response(200, [], file_get_contents(__DIR__.'/fixtures/loadBalancer_metrics.json')));
$apiResponse = $this->load_balancer->metrics('open_connections', '2017-01-01T00:00:00+00:00', '2017-01-01T23:00:00+00:00', 60);
$metrics = $apiResponse->getResponsePart('metrics');

$this->assertEquals([[1435781470.622, '42']], $metrics->time_series->open_connections->values);
$this->assertLastRequestEquals('GET', '/load_balancers/4711/metrics');
}
}
14 changes: 14 additions & 0 deletions tests/Unit/Models/LoadBalancers/LoadBalancersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,18 @@ public function testList()
$this->assertEquals($loadBalancers[0]->name, 'my-resource');
$this->assertLastRequestEquals('GET', '/load_balancers');
}

public function testCreate()
{
$this->mockHandler->append(new Response(201, [], file_get_contents(__DIR__.'/fixtures/loadBalancer_create.json')));
$response = $this->loadBalancers->create('my-load-balancer', 'lb11', 'fsn1');

$this->assertNotNull($response);
$this->assertEquals(4711, $response->load_balancer->id);
$this->assertEquals('my-load-balancer', $response->load_balancer->name);
$this->assertEquals('create_load_balancer', $response->action->command);

$this->assertLastRequestEquals('POST', '/load_balancers');
$this->assertLastRequestBodyParametersEqual(['name' => 'my-load-balancer', 'load_balancer_type' => 'lb11', 'location' => 'fsn1']);
}
}
82 changes: 82 additions & 0 deletions tests/Unit/Models/LoadBalancers/fixtures/loadBalancer_create.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"load_balancer": {
"algorithm": {
"type": "round_robin"
},
"created": "2016-01-30T23:55:00+00:00",
"id": 4711,
"included_traffic": 10000,
"ingoing_traffic": null,
"labels": {},
"load_balancer_type": {
"deprecated": "2016-01-30T23:50:00+00:00",
"description": "LB11",
"id": 1,
"max_assigned_certificates": 10,
"max_connections": 20000,
"max_services": 5,
"max_targets": 25,
"name": "lb11",
"prices": [
{
"location": "fsn1",
"price_hourly": {
"gross": "1.1900000000000000",
"net": "1.0000000000"
},
"price_monthly": {
"gross": "1.1900000000000000",
"net": "1.0000000000"
}
}
]
},
"location": {
"city": "Falkenstein",
"country": "DE",
"description": "Falkenstein DC Park 1",
"id": 1,
"latitude": 50.47612,
"longitude": 12.370071,
"name": "fsn1",
"network_zone": "eu-central"
},
"name": "my-load-balancer",
"outgoing_traffic": null,
"private_net": [],
"protection": {
"delete": false
},
"public_net": {
"enabled": true,
"ipv4": {
"dns_ptr": "lb1.example.com",
"ip": "1.2.3.4"
},
"ipv6": {
"dns_ptr": "lb1.example.com",
"ip": "2001:db8::1"
}
},
"services": [],
"targets": []
},
"action": {
"command": "create_load_balancer",
"error": {
"code": "action_failed",
"message": "Action failed"
},
"finished": "2016-01-30T23:56:00+00:00",
"id": 13,
"progress": 100,
"resources": [
{
"id": 4711,
"type": "load_balancer"
}
],
"started": "2016-01-30T23:55:00+00:00",
"status": "success"
}
}
Loading
Loading