Skip to content

Commit

Permalink
Protecting Your Assets with Replicated (#327)
Browse files Browse the repository at this point in the history
TL;DR
-----

Adds a lab to show license features and custom metrics

Details
-------

Offers a new lab focusing on things you can do with the Replicated 
license. Takes the learner through the following steps:

1. What is the Replicated license?
2. Creating a customer and their license.
3. Using the Replicated proxy registry to protect your private images.
4. Using license Helm values in your templatess. 
5. Custom license entitlements
6. Validating license entitlements with the Replcated SDK
7. Sending custom metrics to measure license usage

The last two steps use shell commands to walk through SDK capabilities a 
vendor will usually work with in their application code. This is a deliberate
choiceto avoid losing some learners based on programming language. We
can consider future labs using the SDK from different languages.
  • Loading branch information
crdant authored May 10, 2024
1 parent fefe8be commit 0334dc1
Show file tree
Hide file tree
Showing 59 changed files with 2,553 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
slug: the-replicated-license
id: frbo6izjilr1
type: challenge
title: The Replicated License
teaser: Understanding the role of the Replicated license
notes:
- type: text
contents: Understanding the Replicated license
tabs:
- title: License File
type: code
hostname: shell
path: /home/replicant
difficulty: basic
timelimit: 400
---

👋 Introduction
===============

The Replicated Platform provides a license that identifies each of your
customers and entitles them to install your software. These customer licenses
provide several default entitlements such as the type of license and whether
(and when) it expries. Replicated provides the license file to you as a
downloadable asset and embeds it into your Helm chart when you include the
Replicated SDK. A digital signature confirms it hasn't been tampered with.

How Licenses Work
================

Each customer you create for your application in the Replicated Vendor Portal
has a license associated with it. The main purpose of the license is to
identify which software the user is entitled to. The entitlement is determined
by two attributes of the license: the application and the release channel. The
platform knows the application the customer was created for, and you specify
the release channel when you create the customer. You can change the channel at
any time. We'll discuss release channels shortly.

The other core attribute of the license is its type. Each customer license has
one of the following types:

* **Development:** The Development type can be used internally by the
development team for testing and integration.
* **Trial:** The Trial type can be used for customers who are on 2-4 week
trials of your software.
* **Paid:** The Paid type identifies the customer as a paying customer for
which additional information can be provided.
* **Community:** The Community type is designed for a free or low cost version
of your application. For more details about this type, see Community
Licenses.

You can change the license type of an existing license. You'll most often do
this when a customer who has been trying your software decides to make a
purchase.

The other common features of all licenses are an expiration date and flags for
enabling several Replicated Platform features. License need not expire, and you
have full control over the expiration date that you set. Replicated features
may change over time and have sensible defaults based on your subscription.

Release Channels
================

Before we create our first customer, let's briefly discuss release channels.
If you've completed another lab like [Avoiding Installation
Pitfalls](https://play.instruqt.com/replicated/tracks/avoiding-installation-pitfalls)
or [Closing the Support Information
Gap](https://play.instruqt.com/replicated/tracks/closing-infromation-gap) then
you may already be familiar with them. If you haven't, then here's a quick introduction.

The [Distributing Your Application with
Replicated](https://play.instruqt.com/replicated/tracks/distributing-with-replicated)
lab covers release channels in the most depth if you'd like more information.

Release channels, at their most basic, map releases to customers. Each customer
is assigned to a specific channel, and any relase can be provided to as many
channels as you want. There are three default channels: `Unstable`, `Beta`, and
`Stable`. Think of `Stable` as your GA software and `Unstable` as releases that
you're planning to test and/or distribute internally. `Beta` is, of course, for
beta releases.

Those are the defaults, but you can have as many channels as you like. Some
vendors drop one of the defaults and only have two channels, while others add
channels for additional stages in the software lifecycle like `Alpha` or `Long
Term Support`. Your team should consider the right set of release channels as
you work through your implementation.

License Files
=============

The customer license is defined in a format that's easy both to read and to
parse. A basic license is in the file `Geeglo.yaml` that you can open in the
editor showing in the "License File" tab. You'll notice that it's a YAML
formatted file with the same structure as a Kubernetes object. The object is
interpreted by the Replicated Platform components rather than the Kubernetes
cluster to avoid needing any special permissions.

![Opening the Geeglo License File](../assets/opening-the-license-file.png)

In most installation scenarios, neither you nor your customer will need to
interact directly with their license file. Instead, the platform embeds it into
install artifacts like the your Helm chart and the Replicated Embedded Cluster
binary. The exception is installing into an existing cluster using the
Replicated Admin Console. The console will prompt for the customer to uploaed
there license as one of the first steps to the install.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/bin/sh
#!/usr/bin/env bash
#
# This script runs when the platform check the challenge.
#
# The platform determines if the script was successful using the exit code of this
# script. If the exit code is not 0, the script fails.
#

kubectl get nodes
set -euxo pipefail
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash

# This set line ensures that all failures will cause the script to error and exit
set -euxo pipefail

HOME_DIR=/home/replicant

# Wait for Instruqt bootstrap to be complete
while [ ! -f /opt/instruqt/bootstrap/host-bootstrap-completed ]
do
echo "Waiting for Instruqt to finish booting the VM"
sleep 1
done

# convenience library for Replicated lab lifecycle scripts
source /etc/profile.d/header.sh

# there's only one app created by the automation, so just grab the first in the list
access_token=$(get_api_token)
app_slug=$(curl --header 'Accept: application/json' --header "Authorization: ${access_token}" https://api.replicated.com/vendor/v3/apps | jq -r '.apps[0].slug')

agent variable set USERNAME $(get_username)

agent variable set PASSWORD $(get_password)
agent variable set REPLICATED_API_TOKEN ${access_token}
agent variable set REPLICATED_APP ${app_slug}

helm show values --version 0.4.1 \
oci://registry.replicated.com/${app_slug}/slackernews 2>/dev/null \
| yq .replicated.license \
> ${HOME_DIR}/Geeglo.yaml

chown replicant ${HOME_DIR}/Geeglo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
#
# This script runs when the platform check the challenge.
#
# The platform determines if the script was successful using the exit code of this
# script. If the exit code is not 0, the script fails.
#

set -euxo pipefail
exit 0

This file was deleted.

101 changes: 101 additions & 0 deletions instruqt/protecting-your-assets/02-create-a-customer/assignment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
slug: create-a-customer
id: f0ayvnopkkyv
type: challenge
title: Creating a Customer License
teaser: Time to add a customer and configure their license
notes:
- type: text
contents: |
Add a customer to your application and configure their license
tabs:
- title: Vendor Portal
type: website
url: https://vendor.replicated.com
new_window: true
- title: Shell
type: terminal
hostname: shell
workdir: /home/replicant
difficulty: basic
timelimit: 600
---


Creating a Customer License
===========================

Let's connect to the Replicated Vendor Portal and create a new customer. This
will also create their license. Click on the "Open External Window" button to
open a new browser window and access the portal. Log in with these credentials:

Username: `[[ Instruqt-Var key="USERNAME" hostname="shell" ]]`<br/>
Password: `[[ Instruqt-Var key="PASSWORD" hostname="shell" ]]`

You'll land on the "Channels" page showing the default release channels and an
added channel for releases with long-term support.

![Vendor Portal Release Channels](../assets/vendor-portal-landing.png)

To create a customer, select "Customers" from the menu on the left, and you'll
see your two existing customers "Omozan" and "Geeglo".

![Your Existing Customers](../assets/customer-landing-page.png)

We're going to assume you're working with a new prospect named "Nitflex" and
create them in the portal. The platform also includes an
[API](https://replicated-vendor-api.readme.io/v3/reference/createapp) you can
use to automate customer creation as part of your existing onboarding workflow.
For the purpose of the lab, click the "+ Create Customer" button to create
"Nitflex" manually.

![Creating a Customer](../assets/create-customer-button.png)

Enter the name "NitFlex" and assign them to the `Stable` channel. They've
trying your software for two months, so let's make sure we capture the expiration
date. We also need an email for them to login and install your Helm chart. Note
that we never use that email, it's your customer, not ours.

Expiration Date: `[[ Instruqt-Var key="LICENSE_EXPIRY" hostname="shell" ]]`<br/>
Customer Email: `[[ Instruqt-Var key="CUSTOMER_EMAIL" hostname="shell" ]]`

![Customer Details](../assets/new-customer-details.png)

You should also specify that they are a trial customer, so select the "Trial"
option for the customer type and save your changes. The process will be
the same for a new paid customer with a longer expiration date and a "Paid"
license type.

Using the License
=================

The Vendor Portal generated a license for your Nitflex customer and also
configured some credentials based on it. These credentials are for:

* The Replicated registry for accessing the Slackernews Helm chart
* Our proxy registry that protects your private images

Let's use the first set of credentials to look at how the license is embedded
into your Helm chart.

Click on "Helm install instructions" and you'll see a popup with a set of
instructions for this customer to install Slackernews. The first command is the
login command for the Helm registry.

![Helm Login Command](../assets/helm-login-command.png)

Use the "Shell" tab in the lab to log into the registry. You'll need to copy
the command from the vendor portal in order to log in. after you login, you can
view the values for the Helm chart using `helm show values`:

`helm show values oci://registry.replicated.com/[[ Instruqt-Var key="REPLICATED_APP" hostname="shell" ]]/slackernews | less`

this will show you the default vales from the Helm chart. As you scroll through
the values, you'll see license information injected in two places. The entire
license file is injected as the `license` value for the `replicated` subchart,
and a few details from it are injected under `.global.replicated`.

One of the global fields is particularly important. The value
`global.replicated.dockerconfigjson` has the required Docker configuration to
access private images using the proxy registry. You will use this value in your
Helm chart to give the cluster access to your private images.
22 changes: 22 additions & 0 deletions instruqt/protecting-your-assets/02-create-a-customer/check-shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# This set line ensures that all failures will cause the script to error and exit
set -euxo pipefail
source /etc/profile.d/header.sh

result=0

# check for release to Unstable
api_token=$(get_api_token)

# get the app id in order to work with the customer
app_id=$(curl --header "Accept: application/json" --header "Authorization: ${api_token}" https://api.replicated.com/vendor/v3/apps | jq -r '.apps[0].id')

# get the customer id in order to assure they exist
new_customer=$(curl --header 'Accept: application/json' --header "Authorization: ${api_token}" "https://api.replicated.com/vendor/v3/app/${app_id}/customers" | jq -r '.customers[] | select( .name == "Nitflex" )')
if [[ -z "${new_customer}" ]] ; then
fail-message $'Please remember to create the customer Nitflex'
let "result = result + 1"
fi

exit ${result}
22 changes: 22 additions & 0 deletions instruqt/protecting-your-assets/02-create-a-customer/cleanup-shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# This set line ensures that all failures will cause the script to error and exit
set -euxo pipefail

### Assure the tmux session exists
#
# In a test scenario Instuqt does not run the user shell for the
# challenge, which means the tmux session is never established. We
# need to session for the solve scripts for other challenges to
# succeed, so let's create it here.
#

if ! tmux has-session -t shell ; then
tmux new-session -d -s shell su - replicant
fi

# clear the tmux pane and scrollback to look like a fresh shell
tmux clear-history -t shell
tmux send-keys -t shell clear ENTER

exit 0
18 changes: 18 additions & 0 deletions instruqt/protecting-your-assets/02-create-a-customer/setup-shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash

# This set line ensures that all failures will cause the script to error and exit
set -euxo pipefail
source /etc/profile.d/header.sh

# get the customer id, since it's the password for the Helm installation
# and users like to copy/paste
api_token=$(get_api_token)
app_id=$(curl --header 'Accept: application/json' --header "Authorization: ${api_token}" https://api.replicated.com/vendor/v3/apps | jq -r '.apps[0].id')

# email for the customer the user will create
new_customer_email=${INSTRUQT_PARTICIPANT_ID}@nitflex.tv
# help the user out by telling them the expiration date for a 30-day trial
license_expiry=$(date -d "+2 months" "+%B %d, %Y")

agent variable set LICENSE_EXPIRY "${license_expiry}"
agent variable set CUSTOMER_EMAIL "${new_customer_email}"
22 changes: 22 additions & 0 deletions instruqt/protecting-your-assets/02-create-a-customer/solve-shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

# This set line ensures that all failures will cause the script to error and exit
set -euxo pipefail
source /etc/profile.d/header.sh

# get the customer id, since it's the password for the Helm installation
# and users like to copy/paste
api_token=$(get_api_token)
app_id=$(curl --header 'Accept: application/json' --header "Authorization: ${api_token}" https://api.replicated.com/vendor/v3/apps | jq -r '.apps[0].id')
app_slug=$(curl --header 'Accept: application/json' --header "Authorization: ${api_token}" https://api.replicated.com/vendor/v3/apps | jq -r '.apps[0].slug')

# provide an email address for the new customer
customer_email="${INSTRUQT_PARTICIPANT_ID}@nitflex.tv"

# create the new customer and keep track of the ID
customer_id=$(replicated customer create --name "Nitflex" --email ${customer_email} --channel Stable --expires-in 1460h --type trial --kots-install=false --output json --app ${app_slug} --token ${api_token} | jq -r .id)

# make sure the customer has a paid license
updated_customer=$(curl --header 'Accept: application/json' --header "Authorization: ${api_token}" "https://api.replicated.com/vendor/v3/app/${app_id}/customer/${customer_id}" | \
jq -c --arg appId "${app_id}" '.customer | {"app_id": $appId, "name": .name, "email": .email, "channel_id": .channels[0].id, "expires_at": .expiresAt, "type": "trial", "is_airgap_enabled": .airgap, "is_gitops_supported": .isGitopsSupported, "is_identity_service_supported": .isIdentityServiceSupported, "is_geoaxis_supported": .isGeoaxisSupported, "is_snapshot_supported": .isSnapshotSupported, "is_support_bundle_upload_enabled": .isSupportBundleUploadEnabled, "entitlementValues":[]}')

Loading

0 comments on commit 0334dc1

Please sign in to comment.