Skip to content

C FFI

Please Read First

Before proceeding, make sure to read this Pulumi Gestalt integrations Overview page to get a better understanding of the documentation.

Artifacts

Shared library and header file are available on the release page

Header file

pulumi_gestalt.h
#ifndef __PULUMI_H__
#define __PULUMI_H__

/* Don't modify this file manually. It is autogenerated by cbindgen. */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct pulumi_composite_output_t pulumi_composite_output_t;

typedef struct pulumi_output_t pulumi_output_t;

typedef struct pulumi_context_t pulumi_context_t;

/**
 * String that may contain nulls and is not null-terminated.
 */
typedef struct pulumi_string_t {
  uint8_t *data;
  uintptr_t len;
} pulumi_string_t;

typedef struct pulumi_object_field_t {
  const char *name;
  const struct pulumi_output_t *value;
} pulumi_object_field_t;

typedef struct pulumi_register_resource_request_t {
  const char *type_;
  const char *name;
  const char *version;
  const struct pulumi_object_field_t *inputs;
  uintptr_t inputs_len;
} pulumi_register_resource_request_t;

typedef struct pulumi_invoke_resource_request_t {
  const char *token;
  const char *version;
  const struct pulumi_object_field_t *inputs;
  uintptr_t inputs_len;
} pulumi_invoke_resource_request_t;

/**
 * Arguments: Engine context, Function context, Serialized JSON value
 * Returned string must represent a JSON value;
 * Library will free the returned string
 */
typedef char *(*pulumi_mapping_function_t)(const void*, const void*, const char*);

typedef enum pulumi_config_value_t_Tag {
  PlainValue,
  Secret,
} pulumi_config_value_t_Tag;

typedef struct pulumi_config_value_t {
  pulumi_config_value_t_Tag tag;
  union {
    struct {
      char *plain_value;
    };
    struct {
      struct pulumi_output_t *secret;
    };
  };
} pulumi_config_value_t;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void pulumi_string_free(struct pulumi_string_t *value);

struct pulumi_context_t *pulumi_create_context(const void *context);

void pulumi_finish(struct pulumi_context_t *ctx);

void pulumi_destroy_context(struct pulumi_context_t *ctx);

struct pulumi_output_t *pulumi_create_output(struct pulumi_context_t *ctx,
                                             const char *value,
                                             bool secret);

struct pulumi_composite_output_t *pulumi_register_resource(struct pulumi_context_t *ctx,
                                                           const struct pulumi_register_resource_request_t *request);

struct pulumi_composite_output_t *pulumi_invoke_resource(struct pulumi_context_t *ctx,
                                                         const struct pulumi_invoke_resource_request_t *request);

struct pulumi_output_t *pulumi_output_map(struct pulumi_context_t *ctx,
                                          const struct pulumi_output_t *output,
                                          const void *function_context,
                                          pulumi_mapping_function_t function);

struct pulumi_output_t *pulumi_output_combine(const struct pulumi_output_t *output,
                                              const struct pulumi_output_t *const *outputs,
                                              uintptr_t outputs_size);

void pulumi_output_add_to_export(const struct pulumi_output_t *value, const char *name);

struct pulumi_output_t *pulumi_composite_output_get_field(struct pulumi_composite_output_t *output,
                                                          const char *field_name);

/**
 * Receives value from configuration
 * `name`: Configuration bag's logical name. If null, the default (project name) is used.
 * `key`: Config key. Cannot be null
 *
 * Returns null when the value is not found
 */
struct pulumi_config_value_t *pulumi_config_get_value(struct pulumi_context_t *ctx,
                                                      const char *name,
                                                      const char *key);

void pulumi_config_free(struct pulumi_config_value_t *value);

/**
 * Returns protobuf encoded schema for the provider.
 * Modules for provider can be found in Pulumi registry on left side with (M) icon:
 * - [AWS](https://www.pulumi.com/registry/packages/aws/)
 * - [Azure](https://www.pulumi.com/registry/packages/azure/)
 * - [GCP](https://www.pulumi.com/registry/packages/gcp/)
 *
 * Empty modules list means that no modules are used.
 * To use all modules, pass null for the modules pointer and 0 for the size.
 */
struct pulumi_string_t *pulumi_get_schema(const char *provider_name,
                                          const char *provider_version,
                                          const char *const *modules,
                                          uintptr_t modules_size);

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#endif  /* __PULUMI_H__ */

Example

main.c
#include <pulumi_gestalt.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static char* mapper(const void* context_context, const void* context, const char* content) {

    const char* function_name = (const char*) context;

    if (strcmp(function_name, "double") == 0) {
        int i = atoi(content);
        int i2 = i * 2;

        char *buffer = malloc(23 * sizeof(char));
        if (buffer == NULL) {
            printf("Cannot allocate buffer");
            exit(2);
        }

        snprintf(buffer, 23, "%d", i2);
        return buffer;
    }
    else if (strcmp(function_name, "static") == 0) {
        return strdup("\"my_string\"");
    }

    printf("Cannot find valid function\n");
    exit(2);
}

static void generate_random_value(pulumi_context_t* ctx) {

    pulumi_output_t* output = pulumi_create_output(ctx, "16", false);

    const pulumi_object_field_t inputs[] = {
    {
        .name = "length",
        .value = output
    }
    };

    const pulumi_register_resource_request_t register_resource_request = {
        .type_ = "random:index/randomString:RandomString",
        .name = "my_name",
        .version = "4.15.1",
        .inputs = inputs,
        .inputs_len = 1,
    };

    pulumi_composite_output_t* composite_output = pulumi_register_resource(ctx, &register_resource_request);

    pulumi_output_t* output_result = pulumi_composite_output_get_field(composite_output, "result");

    pulumi_output_add_to_export(output_result, "result");
}

static void run_command(pulumi_context_t* ctx) {
    pulumi_output_t* output = pulumi_create_output(ctx, "\"whoami\"", false);

    const pulumi_object_field_t inputs[] = {
        {
            .name = "command",
            .value = output
        }
    };

    const pulumi_invoke_resource_request_t register_resource_request = {
        .token = "command:local:run",
        .version = "1.0.2",
        .inputs = inputs,
        .inputs_len = 1,
    };

    pulumi_composite_output_t* output_2 = pulumi_invoke_resource(ctx, &register_resource_request);

    pulumi_output_t* stdout_output = pulumi_composite_output_get_field(output_2, "stdout");

    pulumi_output_add_to_export(stdout_output, "whoami_stdout");
}

static void perform_operations_on_outputs(pulumi_context_t* ctx) {

    pulumi_output_t* output = pulumi_create_output(ctx, "16", false);

    pulumi_output_t* output_2 = pulumi_output_map(ctx, output, "double", &mapper);
    pulumi_output_t* output_3 = pulumi_output_map(ctx, output, "static", &mapper);

    const pulumi_output_t* arr[] = { output_2, output_3 };
    pulumi_output_t* output_4 = pulumi_output_combine(output, arr, 2);

    pulumi_output_add_to_export(output_2, "double_length");
    pulumi_output_add_to_export(output_3, "static_string");
    pulumi_output_add_to_export(output_4, "combined");
}

static void perform_operations_on_default_config(pulumi_context_t* ctx) {
    pulumi_config_value_t* non_existing = pulumi_config_get_value(ctx, NULL, "test");
    if (non_existing != NULL) {
        printf("NULL was expected but not returned");
        exit(2);
    }
    pulumi_config_value_t* plaintext = pulumi_config_get_value(ctx, NULL, "plaintext");
    if (plaintext->tag != PlainValue) {
        printf("PlainText tag was expected but not returned");
        exit(2);
    }
    if (strcmp(plaintext->plain_value, "plain_value")) {
        printf("plain_value was expected but not returned. Returned value is [%s]", plaintext->plain_value);
        exit(2);
    }
    pulumi_config_free(plaintext);
    pulumi_config_value_t* secret = pulumi_config_get_value(ctx, NULL, "secret");
    if (secret->tag != Secret) {
        printf("Secret tag was expected but not returned");
        exit(2);
    }
    pulumi_output_add_to_export(secret->secret, "secret");
    pulumi_config_free(secret);
}

static void perform_operations_on_custom_config(pulumi_context_t* ctx) {
    pulumi_config_value_t* non_existing = pulumi_config_get_value(ctx, "namespace", "test");
    if (non_existing != NULL) {
        printf("NULL was expected but not returned");
        exit(2);
    }
    pulumi_config_value_t* plaintext = pulumi_config_get_value(ctx, "namespace", "plaintext");
    if (plaintext->tag != PlainValue) {
        printf("PlainText tag was expected but not returned");
        exit(2);
    }
    if (strcmp(plaintext->plain_value, "plain_value_namespace")) {
        printf("plain_value_namespace was expected but not returned. Returned value is [%s]", plaintext->plain_value);
        exit(2);
    }
    pulumi_config_free(plaintext);
    pulumi_config_value_t* secret = pulumi_config_get_value(ctx, "namespace", "secret");
    if (secret->tag != Secret) {
        printf("Secret tag was expected but not returned");
        exit(2);
    }
    pulumi_output_add_to_export(secret->secret, "secret_namespace");
    pulumi_config_free(secret);
}

static void obtain_schema() {
    pulumi_string_t* schema = pulumi_get_schema("docker", "4.5.3", NULL, 0);
    if (schema == NULL) {
        printf("Cannot obtain schema");
        exit(2);
    }
    if (schema->data == NULL || schema->len == 0) {
        printf("Schema is empty");
        exit(2);
    }
    pulumi_string_free(schema);
}

int main() {
    pulumi_context_t* ctx = pulumi_create_context(NULL);

    run_command(ctx);
    perform_operations_on_outputs(ctx);
    generate_random_value(ctx);
    perform_operations_on_default_config(ctx);
    perform_operations_on_custom_config(ctx);
    obtain_schema();

    pulumi_finish(ctx);
    pulumi_destroy_context(ctx);
}

Cleanup

Output and CompositeOutput does not need to be explicitly cleaned up. They are automatically managed by the SDK and will be destroyed when the context is destroyed.

Example

Here is an example written in C++ that uses C FFI: https://github.com/andrzejressel/pulumi-gestalt/blob/main/examples/cpp/main.cpp