LXD PHP Client Documentation v0.0.1
Class Lxd Endpoints

Images

/*
 +------------------------------------------------------------------------+
 | PHP LXD Extension                                                      |
 +------------------------------------------------------------------------+
 | Copyright (c)2017-2017 LXC.systems (https://github.com/lxc-systems/lxd)|
 +------------------------------------------------------------------------+
 | This source file is subject to GNU General Public License v2.0 License |
 | that is bundled with this package in the file LICENSE.                 |
 |                                                                        |
 | If you did not receive a copy of the license and are unable to         |
 | obtain it through the world-wide-web, please send an email             |
 | to license@lxd.systems so we can send you a copy immediately.          |
 +------------------------------------------------------------------------+
 | Authors: Lawrence Cherone                        |
 +------------------------------------------------------------------------+
 */

namespace Lxd\Endpoints;

use Lxd\Endpoint;

/**
 * Lxd\Endpoints\Images
 *
 * Provides image facilities to the API
 * @see https://github.com/lxc-systems/lxd/blob/master/lxd/endpoints/images.zep
 */
final class Images extends Endpoint
{    
    /**
     * @var - Base API endpoint
     */
    const ENDPOINT = "images";

    /**
     * @var
     */
    protected curl;

    /**
     * Class construct.
     *
     * @param  array          config Config array which holds object configuration
     * @param   curl
     * @return void
     */
    public function __construct(array! config,  curl) -> void
    {
        parent::__construct(config, curl, __CLASS__);
    }

    /**
     * List of all images.
     *
     * 
     *  // images on connected server
     *  $lxd->images->all();
     *
     *  // images on public image server. (URL must include /1.0/images)
     *  $lxd->images->all("https://images.linuxcontainers.org:8443/1.0/images");
     * 
     *
     * @return array
     */
    public function all(string! url = null) -> array
    {
        var key, value, response = [
            "metadata": []
        ];

        if url === null {
            let url = (string) this->getBase(Images::ENDPOINT);
        }

        if parse_url(url, PHP_URL_PATH) !== "/".this->getVersion()."/images" {
            throw "Invalid image server URL";
        }

        let response = this->curl->get(
            url
        );

        if response["type"] === "error" {
            return response;
        }

        for key, value in response["metadata"] {
            let response["metadata"][key] = str_replace(
                "/".this->getVersion()."/".Images::ENDPOINT."/", null, value
            );
        }

        return response;
    }

    /**
     * Get image info.
     *
     * 
     *  // image info on connected server
     *  $lxd->images->info(null, "images-fingerprint", "secret");
     *
     *  // images on public image server. (URL must include /1.0/images)
     *  $lxd->images->info("https://uk.images.linuxcontainers.org:8443/1.0/images", "images-fingerprint"));
     * 
     *
     * @param  null|string  url          null or full URL to public image endpoint
     * @param  string       fingerprint  Fingerprint of image
     * @param  string       secret       Secret for untrusted client
     * @return array
     */
    public function info(string! url = null, string! fingerprint, string! secret = null) -> array
    {
        var param = [];

        if url === null {
            let url = (string) this->getBase(Images::ENDPOINT);
        }

        if parse_url(url, PHP_URL_PATH) !== "/".this->getVersion()."/images" {
            throw "Invalid image server URL";
        }

        if !empty(secret) {
            let param["secret"] = secret;
        }

        return this->curl->get(url."/".fingerprint, param);
    }

    /**
     * Create a new image.
     *
     * 
     *  $lxd->images->create(
     *      [],  // image options
     *      [],  // API headers
     *      true // wait for operation
     *  );
     * 
     *
     * @param  array    options  New image options
     * @param  array    headers  Endpoint headers
     * @param  boolean  wait     Wait for image to create
     * @return array
     */
    public function create(array! options, array! headers = [], boolean! wait = false) -> array
    {
        var response;

        let response = this->curl->post(
            this->getBase(Images::ENDPOINT), 
            options,
            headers
        );

        if response["type"] !== "error" && wait {
            let response = this->curl->get(
                this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait",
                [
                    "timeout" : this->config["timeout"]
                ]
            );
        }

        return response;
    }

    /**
     * Create from remote.
     *
     * 
     *  // Import an image by alias
     *  $lxd->images->createFromRemote(
     *      "https://images.linuxcontainers.org:8443",
     *      [
     *          "alias" => "ubuntu/xenial/amd64",
     *      ]
     *  );
     *
     *  // Import an image by fingerprint
     *  $lxd->images->createFromRemote(
     *      "https://images.linuxcontainers.org:8443",
     *      [
     *          "fingerprint" => "b0f4faff46c9cb02db10984e2cf71c62fd539a9ab680d6fd54955671f3186087",
     *      ]
     *  );     
     *
     *  // Import an image by fingerprint, set auto update and wait for pull
     *  $lxd->images->createFromRemote(
     *      "https://images.linuxcontainers.org:8443",
     *      [
     *          "fingerprint" => "b0f4faff46c9cb02db10984e2cf71c62fd539a9ab680d6fd54955671f3186087",
     *      ],
     *      true,
     *      true
     *  );
     * 
     *
     * @param  array    options  New image options
     * @param  array    headers  Endpoint headers
     * @param  boolean  wait     Wait for image to create
     * @return array
     */
    public function createFromRemote(
        string! server,
        array! options,
        boolean! autoUpdate = false,
        boolean! wait = false
    ) -> array
    {
        var opts;
        var source = this->getSource(options);

        if isset(options["protocol"]) && !in_array(options["protocol"], ["lxd", "simplestreams"]) {
            throw "Invalid protocol.  Valid choices: lxd, simplestreams";
        }

        var only = [
            "secret",
            "protocol",
            "certificate"
        ];

        var remoteOptions = array_intersect_key(options, array_flip(only));

        let opts                     = this->getOptions($options);
        let opts["auto_update"]      = autoUpdate;
        let opts["source"]           = array_merge(source, remoteOptions);
        let opts["source"]["type"]   = "image";
        let opts["source"]["mode"]   = "pull";
        let opts["source"]["server"] = server;

        return this->create(opts, [], wait);
    }

    /**
     * Create an image from a container.
     *
     * 
     *  // Create a private image from container.
     *  $lxd->images->createFromContainer('container-name');
     *
     *  // Create a public image from container
     *  $lxd->images->createFromContainer(
     *      'container-name',
     *      [
     *          'public' => true
     *      ]
     *  );     
     *
     *  // Create an image from container, store properties with the new image and override its filename.
     *  $lxd->images->createFromContainer(
     *      'container-name',
     *      [
     *          'filename'   => 'ubuntu-trusty.tar.gz',
     *          'properties' => ['os' => 'Ubuntu'],
     *      ]
     *  );
     * 
     *
     * @param  array    options  New image options
     * @param  array    headers  Endpoint headers
     * @param  boolean  wait     Wait for image to create
     * @return array
     */
    public function createFromContainer(string! name, array! options, boolean! wait = false) -> array
    {
        var opts = [];
        let opts                   = this->getOptions(options);
        let opts["source"]["type"] = "container";
        let opts["source"]["name"] = name;

        return this->create(opts, [], wait);
    }

    /**
     * Create an image from a snapshot.
     *
     * 
     *  // Create a private image from snapshot
     *  $lxd->images->createFromSnapshot("container_name", "snapshot_name");
     *
     *  // Create a public image from snapshot
     *  $lxd->images->createFromSnapshot(
     *      "container_name",
     *      "snapshot_name",
     *      [
     *          "public" => true
     *      ]
     *  );
     *
     *  // Store properties with the new image, and override its filename
     *  $lxd->images->createFromSnapshot(
     *      "container-name",
     *      "snapshot-name",
     *      [
     *          "filename"   => "ubuntu-trusty.tar.gz",
     *          "properties" => ["os" => "Ubuntu"]
     *      ]
     *  );
     * 
     *
     * @param  string  container The name of the container
     * @param  string  snapshot  The name of the snapshot
     * @param  array   options   Options to create the container
     * @param  bool    wait      Wait for operation to finish
     * @return array
     */
    public function createFromSnapshot(string! container, string! snapshot, array! options, boolean! wait = false) -> array
    {
        var opts = [];
        let opts                   = this->getOptions(options);
        let opts["source"]["type"] = "snapshot";
        let opts["source"]["name"] = container."/".snapshot;

        return this->create(opts, [], wait);
    }

    /**
     * Replace the configuration of a image.
     *
     * Configuration is overwritten, not merged. Clients should
     * first call the info method to obtain the current configuration of a
     * image. The resulting objects metadata should be modified and then passed to
     * the update method.
     *
     * 
     *  $image = $lxd->images->info(null, 'b0f4faff46c9cb02db10984e2cf71c62fd539a9ab680d6fd54955671f3186087');
     *  $image['metadata']['public'] = true;
     *  $lxd->images->replace('container-name', $image['metadata']);
     * 
     *
     * @param  string   fingerprint  Fingerprint of image
     * @param  array    options      Options to replace
     * @param  boolean  wait         Wait for operation to finish
     * @return array
     */
    public function replace(string! fingerprint, array! options, boolean! wait = false) -> array
    {
        var response;

        let response = this->curl->put(
            this->getBase(Images::ENDPOINT)."/".fingerprint, 
            options
        );

        if response["type"] !== "error" && wait {
            let response = this->curl->get(
                this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait",
                [
                    "timeout" : this->config["timeout"]
                ]
            );
        }

        return response;
    }

    /**
     * Delete an image.
     *
     * 
     *  $lxd->images->remove('b0f4faff46c9cb02db10984e2cf71c62fd539a9ab680d6fd54955671f3186087', true);
     * 
     *
     * @param  string  fingerprint  Fingerprint of image
     * @param  bool    wait         Wait for operation to finish
     * @return array
     */
    public function remove(string! fingerprint, boolean! wait = false) -> array
    {
        var response;
        let response = this->curl->delete(this->getBase(Images::ENDPOINT)."/".fingerprint);

        if response["type"] !== "error" && wait {
            let response = this->curl->get(
                this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait",
                [
                    "timeout" : this->config["timeout"]
                ]
            );
        }

        return response;
    }

    /**
     * Get image source attribute.
     *
     * @param  array  options  Options for creating image
     * @return array
     */
    final private function getSource(array! options) -> array|
    {
        var attr, attrs = [
            "alias", "fingerprint"
        ];

        for attr in attrs {
            if isset options[attr] && !empty options[attr] {
                return [attr : options[attr]];
            }
        }

        throw "Alias or Fingerprint must be set";
    }    

    /**
     * Get the options for creating image.
     *
     * @param  string name    Name of image
     * @param  array  options Options for creating image
     * @return array
     */
    final private function getOptions(array! options) -> array
    {
        var only = [
            "filename",
            "public",
            "properties",
            "auto_update"
        ];

        return (array) array_intersect_key(options, array_flip(only));
    }

}