top of page
Writer's pictureAna Maria Mihalceanu

Building a Cloud Compute Instance with Java Concepts

Updated: Jul 24


a dog resting on a laptop

While infrastructure configuration management tools have been around since the 1970s, the rise in popularity of DevOps and cloud computing brought a new perspective to how we can all provision and maintain infrastructure using our favorite programming language. In this post, I will disclose how I build Oracle Cloud Infrastructure (OCI) Instances with Java and Oracle Cloud Infrastructure (OCI) provider for Pulumi.


WARNING: At the time of writing this article, Pulumi Java support is in public preview.


Goals and Prerequisites


Before going into action, let's discuss what we want to achieve:

  • Have a Java project running on a recent JDK release (at the moment I am writing this is 21) that builds one or more OCI instances.

  • As environment parameters can vary, would be great to maintain them in one or more .properties or .yaml configuration files.

There are several SDKs in the market that support building OCI components with Java, including an Oracle Cloud Infrastructure SDK for Java, but I have seen a few presentations about Pulumi at several conferences in 2023, so I thought to give it a try.

Here is the full lists of ingredients for the project:


Once you have those, you can proceed with initializing and customizing your project.


Initialize and Polish Your Maven Project


Every Pulumi project is deployed to a stack, meaning as an isolated and independently configurable instance. Each stack has its state, and Pulumi uses it to know when and how to change cloud resources. Let's create a home directory where you will store a state file and your stack source code, for example by running the following command in a terminal window:

mkdir oci-vm-provider

To track changes in your infrastructure, Pulumi stores the state of your Stack in Pulumi Service or a local state file. In this article, the state is stored in a local state file by running the following commands:

mkdir pulumi-state-local
pulumi login file://pulumi-state-local

Now we can generate a new Java project that uses maven by running:

 pulumi new java --name oci-vm-provider --force

Keep the defaults suggested by the tool and you will obtain a maven Java project running with minimum Java 11. For the project to use the OCI provider for Pulumi, add also the dependency:

       <dependency>
            <groupId>com.pulumi</groupId>
            <artifactId>oci</artifactId>
            <version>${oci.version}</version>
        </dependency>

If you want to run your project with Java 21+, you must change the maven-compiler-plugin setup and add an extra configuration in order to try some preview features too:

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

Pulumi stores the name, runtime and project description in the Pulumi.yaml file. The file named Pulumi.<stack-name>.yaml contains key-value pairs for credentials and other specific configurations (like amount of instances to create):

config:
  oci-vm-provider:amount_vm: 1
  oci-vm-provider:compartment_ocid: your-parent-compartment
  oci-vm-provider:operating_system: Oracle Linux
  oci-vm-provider:operating_system_version: "9"
  oci-vm-provider:routetable_displayname: demo_routetable
  oci-vm-provider:subnet_cidr: 10.0.0.0/28
  oci-vm-provider:subnet_displayname: demo_subnet
  oci-vm-provider:subnet_dns_label: demoendpoints
  oci-vm-provider:internetgateway_name: demo_internetgateway
  oci-vm-provider:ssh_authorized_keys: your-ssh-key
  oci-vm-provider:ssh_private_key_file: your-private-key-file
  oci-vm-provider:vcn_cidr_block: 10.0.0.0/16
  oci-vm-provider:vcn_display_name: demo_vcn
  oci-vm-provider:vcn_dns_label: demodns

Some of those details, such as oci-vm-provider:ssh_authorized_keys, require for you to modify them. You can do that one by one by running :

# you will need the passphrase when encrypting configurations export PULUMI_CONFIG_PASSPHRASE=<your-passphrase>

pulumi config set oci:region <your-oci-region> #the config has us-phoenix-1 default

pulumi config set oci:tenancyOcid <your-oci-tenancy-id> --secret #encrypt your tenancy OCID

pulumi config set oci:userOcid <your-oci-user-id> --secret #encrypt your user OCID

pulumi config set oci:fingerprint <your-fingerprint> --secret pulumi config set compartment_ocid <your-compartment-ocid>
cat <local-path-to-privateKey> | pulumi config set oci:privateKey --secret

or you can run the setup.sh script provided in the repository. The script will ask you for several credentials details. You can find these credentials by going to the Oracle Cloud Console user interface:
  1. Click the Profile menu and select your user (usually your email address).
  2. In your profile you have an area named Capabilities, containing a link to a configuration file.
  3. Click the link and copy their following values:
    • region where you wish to provision the infrastructure;
    • tenancy to fulfill your tenancy OCID;
    • user for your userOcid;
    • privateKey to sign in the API. In case you don’t have an API signing key, checkout the steps here.
    • fingerprint to fill in your oci fingerprint. This value is associated to your privateKey;
  4. To fill in your compartment OCID, you can see that by going to Identity in your menu.

To make sure that the project is correct from infrastructure and Java point of view, you can run:

 pulumi preview

The generated project contains a Java class with the following content:


In the main method, you can call Pulumi.run and pass it a method that will construct the resources you wish to deploy:


You can use the values from Pulumi.<stack-name>.yaml via the config variable. Yet, a few infrastructure components require to be built, and placing them all together in the same method would create a cluttered entrypoint. So let's see how we can elegantly split the responsibilities.


Represent Configurations with Records and Sealed Classes


The SDK provides classes to access or create infrastructure resources independently. Yet,

as this Java project creates a piece of infrastructure putting together all these independent parts, it is better to describe those with language constructs that will help to this purpose:



To build an OCI Compute Instance you need the following immediate components:

  • An availability domain which consists of a set of data centers within an Oracle Cloud Infrastructure region where your compartment and future instance reside;

  • A compute shape which is a template that determines the number of OCPUs , amount of memory, and other resources that are allocated to that instance;

  • A platform image that determines the operating system and other software for that instance;

  • An available Subnet in your compartment.

These components are related to the structure of the Instance, hence you can choose to represent their class hierarchy as allowed heirs of InstanceStructure sealed interface:


To find existing compute shapes in OCI you need an availability domain, so you can model the two components as nested record patterns:


Similarly, you should determine a suitable platform image for that compute shape, so PlatformImage is also a record containing a ComputeShape field:


The project can build a subnet in the given compartment. A subnet is a logical part of a Virtual Cloud Network (VCN) that requires a few components as well:

  • A Virtual Cloud Network (VCN) described by the definition of the VirtualNetwork record.

  • Your VCN will need access to the internet and the NetworkGateway record describes that resource.

  • The subnet needs rules for to and from internet access and the RouterTable record has a NetworkGateway field to represent that.

The subnet itself is described by Subnetwork record that contains a RouterTable field:


These records belong to SubnetStructure hierarchy as you need them to build a subnet:


Now that you caught a glimpse of the class hierarchy, let's look at the operations required to work with these records.


Define Infrastructure Operations with Pattern Matching


As availability domains, compute shapes and platform images are part of the built-in Oracle Cloud Infrastructure, one would need just to find them. In consequence, you can use pattern matching for switch to find any of these InstanceStructure records:



Similarly, building the instance structure is done via a pattern matching for switch on each SubnetStructure type:


You can find the entire code of the project and instructions on how to run it by going to its Github repository: https://github.com/ammbra/oci-vm-provider/.


Replicate Project Setup for Many Environments


To run the existing project, you just simply run the following command in a terminal window:

pulumi up

If you want to deploy to another environment, easily create a new stack by running:

pulumi stack

The above command will end up creating a new Pulumi.<stack_name>.yaml where you can store new cloud credentials, such as a new compartment id where you need to create compute instances. When you no longer need the resources within a specific stack, you can run:

pulumi destroy

Thoughts to ponder


While this project is easy to run from your computer, managing certificates and other secrets from your local can become a tedious operation in the long term. One possible way to evolve the current setup is to have Github workflows that execute all the command line steps provided in this article. Yet, if you are searching for a one-time spin of resources and enjoy coding with Java, give this tutorial a try. Have fun 😉!

82 views0 comments

Recent Posts

See All

Comments


bottom of page