C# でOpenStack Keystoneへアクセス

参考↓

www.conoha.jp

オブジェクトストレージにアクセスする前に、トークンの取得をC#でやってみます。

ConoHaはOpenStack準拠しているようですので、まずは認証をするためにOpenStack Keystoneの仕様を読んでリクエストを投げます。

OpenStack Docs: Welcome to keystone’s documentation!

HttpClientでJson POSTリクエストを生成

HttpClientは罠が多いですが以下のようなコードを書きます。ポイントはHttpClientをstaticで持つところです。以前のWebClientと比べると何か違和感がありますが...。

"Content-Type"に"application/json"を指定して結果をjsonで受け取れるようにすることを忘れないでおきます。

public static class WebExtension {
    private static HttpClient httpClient = new HttpClient();

    public static HttpClient AddHeader(this HttpClient client, string name, string value) {
        client.DefaultRequestHeaders.TryAddWithoutValidation(name, value);
        return client;
    }
    public static HttpClient AddAcceptHeader(this HttpClient client, string header = "application/json") => client.AddHeader("Accept", header);
    public static HttpClient AddContentHeader(this HttpClient client, string header = "application/json") => client.AddHeader("Content-Type", header);
    public static HttpClient AddAuthToken(this HttpClient client, string token) => (token != null) ? client.AddHeader("X-Auth-Token", token) : client;


    public static Task<HttpResponseMessage> Post(string url, string jsonStr, string authToken = null) {
        httpClient.AddContentHeader().AddAuthToken(authToken);

        var content = new StringContent(jsonStr, Encoding.UTF8, "application/json");
        return httpClient.PostAsync(url, content);
    }
    public static Task<HttpResponseMessage> Post(string url, object data, string authToken = null) => Post(url, JsonConvert.SerializeObject(data), authToken);
}

送るデータをクラスを作成せずに用意する

Newtonsoft.JsonのJObjectをdynamicにキャストして使用するとjavascriptのような記述ができます。

contentをJsonからクラスにデシリアライズすれば完了です。

public class KeyStone {
    public string AuthUrl { get; protected set; }

    protected KeyStone() { }
    public static async Task<KeyStone> Authenticate(string url, string tenant, string user, string pass) {
        var data = new JObject() as dynamic;
        data.auth = new JObject() as dynamic;
        data.auth.tenantName = tenant;
        data.auth.passwordCredentials = new JObject() as dynamic;
        data.auth.passwordCredentials.username = user;
        data.auth.passwordCredentials.password = pass;

        HttpResponseMessage result = await WebExtension.Post(url, data, authToken: null);
        var content = await result.Content.ReadAsStringAsync();
        if (result.StatusCode != System.Net.HttpStatusCode.OK) {
            throw new HttpRequestException(result.ToString());
        }

        return new KeyStone() {
            AuthUrl = url,
            // Token
        };
    }
}

テスト結果

VSでクラスにキャストしましたが以下のようなデータが帰ってくれば成功です。

TokenとEndpointの内容を使ってOpenStack準拠の他のサービスにアクセスできます。

public class Rootobject {
    public Access access { get; set; }
}

public class Access {
    public Token token { get; set; }
    public Servicecatalog[] serviceCatalog { get; set; }
    public User user { get; set; }
    public Metadata metadata { get; set; }
}

public class Token {
    public DateTime issued_at { get; set; }
    public DateTime expires { get; set; }
    public string id { get; set; }
    public Tenant tenant { get; set; }
    public string[] audit_ids { get; set; }
}

public class Tenant {
    public string domain_id { get; set; }
    public string description { get; set; }
    public bool enabled { get; set; }
    public string id { get; set; }
    public string name { get; set; }
}

public class User {
    public string username { get; set; }
    public object[] roles_links { get; set; }
    public string id { get; set; }
    public Role[] roles { get; set; }
    public string name { get; set; }
}

public class Role {
    public string name { get; set; }
}

public class Metadata {
    public int is_admin { get; set; }
    public string[] roles { get; set; }
}

public class Servicecatalog {
    public Endpoint[] endpoints { get; set; }
    public object[] endpoints_links { get; set; }
    public string type { get; set; }
    public string name { get; set; }
}

public class Endpoint {
    public string region { get; set; }
    public string publicURL { get; set; }
}