Exploring Tanzu Mission Control API

Mike Carpendale
7 min readFeb 13, 2023

--

Photo by Martin Wyall on Unsplash

Almost mission impossible………

Welcome to my series of ramblings ……. today I’ll dive into the Tanzu Mission Control API. There is a part II of this, check it out here

Despite its potential, exploring the TMC API can be challenging due to the limited documentation available, and its alpha status. In this blog, I aim to help you along your TMC API development process by sharing what I found, where I got stuck and how I got unstuck and thank those who helped me. Hopefully making it easier for you to navigate the Tanzu Mission Control API, leveraging it to enhance your operations. Whether you’re an experienced developer or a beginner, I hope that this blog will be a valuable resource for you.

The Background — As a Platform Field Solution Architect at Pure Storage, I regularly develop demos that showcase not only our technology but also the technology of our alliance partners.

One such demo involves migrating a cloud-native application with stateful data from an on-premise environment to a web-scale cloud provider using Portworx. You can check this demo out here in detail — WIP

The single-click migration window has an animated story to help explain the process, but I also have to keep track of the website that adds stateful data to the cloud-native app and the TMC website — before and after — proof it ain’t all smoke and mirrors.

It isn’t bad for my first attempt — but upon reflection, I could do better. I want to simplify the proof process, the windows on display, and the overall visual experience of this demo.

This is why I am exploring the TMC API, integrating it to my single-click migration web page— less windows, less fuss — well BIG fuss that I just migrated my cloud native app from on-prem to a webscale cloud — but you get the idea, right…….

Your mission, Mike should you choose to accept it, is to add the status of your TKG PODs to your single-click web page……….
So normally it’s the message that self-destructs, right — but I felt like it was going to be me after a little bit………

This is how I felt after an hour of accepting the mission — Photo by Yosh Ginsu on Unsplash

So when you don’t know something, what do we do — we turn to Google search, “Hmmmmmm this isn’t good”….. is my initial thought, as a couple of results popped up; here’s what I found……

Thanks to Dean — I got his postman collection working from here https://veducate.co.uk/postman-tmc-api/
But only cluster list. This is limited to some of the easier API Calls. And that’s fair — Dean’s created this collection on his own accord……. cause the official one is……..

As Dean notes “If you want to explore the APIs more, you can download and import the Swagger/Open API spec from VMware yourself and import into Postman, but personally I found this hard to work with”. Copy that….. boy it was frustrating.

I then turned to my second search and read the great info from Eknath. I can get CMDL stuff working thanks to how Eknath laid it all out — really nice!
http://captainvirtualization.com/tmc-api/
But again this is limited to some of the easier API calls

Auth with token

refresh_token="replace with your Token"
access_token=$(curl -d "refresh_token=$refresh_token" https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize | jq -r '.access_token')

Get the clusters

mc@mc-mpb16 tmc % curl -s 'searchScope.name=*' https://<your-name>.tmc.cloud.vmware.com/v1alpha1/clusters -H "Authorization: Bearer $access_token" | jq '.clusters[].fullName.name'

"sg-tkg-aws-demo2"
"sg-tkg-vsp-demo05"
"sg-tkg-vsp-demo06"

I wanted more — the workload names, the object type, and the number of PODs in the workloads all associated with specific clusters

I then turned to the actual VMware documentation here
It’s really confusing…….. and I’m essentially stuck trying to drill down into the Clusters, and namespace on each to get the Workloads and the PODs — this is where I’m coming unstuck.

Fortunately, I was included in the TMC Starter program and met some wonderful people — “I’m sure they could help or will know someone who can”. And sure enough, after dropping Morgan Fine a note, I got connected to Corey Dinkens, a dead-set legend and Architect in the Tanzu Technical Marketing team.

Corey gave me some tips 👇 that got me up and running straight away! Thanks Corey!! 🍻 (and as it turns out, we both did a write up on this — Corey’s version here)

This example will retrieve the workload objects for a cluster; notice the pagination.size for results returned, and specific mentions of the managementClusterName, provisionerName, and cluster name (in the URL) as these scope the query to a single cluster:

curl -s 'searchScope.name=*' 'https://<your-name>.tmc.cloud.vmware.com/v1alpha1/clusters/sg-tkg-vsp-demo05/objects?sort_by=fullName.name%3Aascending&includeTotalCount=true&query=data.kind%3A%5B%27Deployment%27%2C+%27ReplicaSet%27%2C+%27StatefulSet%27%2C+%27DaemonSet%27%5D&search_scope.managementClusterName=sg-tkg-vsp-mgmt01&search_scope.provisionerName=default&pagination.size=200&pagination.offset=0' -H "Authorization: Bearer $access_token" | jq '.objects[].fullName.name'

"agent-updater-5bf75477f5"
"agent-updater-68fcc9fcc8"
"agent-updater-7d54cf57"
....snip
"portworx-api"
"portworx-operator"
"portworx-operator-584445cdf5"
"postgres"
"postgres-5c6d847dd"
"prometheus-px-prometheus"
"px-csi-ext"
"px-csi-ext-785b966cf8"
"px-prometheus-operator"
"px-prometheus-operator-7669848d99"
"secretgen-controller"
"secretgen-controller-57d4f5985f"
"stork"
"stork-8dcc4bf5"
"stork-scheduler"
"stork-scheduler-794d7fc67c"
"sync-agent"
"sync-agent-5d4c8dff45"
"sync-agent-66dbbd6db"
"tanzu-capabilities-controller-manager"
"tanzu-capabilities-controller-manager-954bb6b47"
"tmc-observer"
"tmc-observer-546667c499"
"tmc-observer-769bc976f5"
"tmc-observer-f7856dd7d"
"vsphere-cloud-controller-manager"
"vsphere-csi-controller"
"vsphere-csi-controller-5c944954c6"
"vsphere-csi-node"

Let's explore the import sections in bold as to what Corey told me;

curl -s ‘searchScope.name=*’ ‘https://<your-sub-name>.tmc.cloud.vmware.com/v1alpha1/clusters/sg-tkg-vsp-demo05/objects?sort_by=fullName.name%3Aascending&includeTotalCount=true&query=data.kind%3A%5B%27Deployment%27%2C+%27ReplicaSet%27%2C+%27StatefulSet%27%2C+%27DaemonSet%27%5D&search_scope.managementClusterName=sg-tkg-vsp-mgmt01&search_scope.provisionerName=default&pagination.size=200&pagination.offset=0' -H “Authorization: Bearer $access_token” | jq ‘.objects[].fullName.name’

  • My TKG Cluster I want info from is sg-tkg-vsp-demo05
  • The management cluster is needed as well and is sg-tkg-vsp-mgmt01
  • The provisioner where my TGK cluster is deployed to in TMC is default

THEN — Corey gave me this……… GOLD

You can use the Chrome/Edge network inspector to do the hard work for you to see the request and headers generated from the UI and use them to build your queries.

Steps to get a specific API request (or to figure out which API is being used): Browse to the view you want the data from, F12 or open Dev tools, select the ‘Network’ tab and make sure the red record button is on, then click the link for the desired view or refresh and look for the corresponding API call. As you can see below under the request and headers, you have the full query string that you can start using in scripts

Let's jump into my TMC UI and navigate to my specific view so I can capture that URL to use in my curl string.
Below 👇 I’ve navigated to the Workload section, applied a Namespace filter: migration, to show me only workloads from the migrationnamespace Namespace

Now I’ve applied an additional filter to Cluster, filter=vsp showing only my TKG Clusters in my vSphere env

This allowed me to get the URL and add it to my curl string. Let's take a look at what we have

  • The TMC UI view shows workloads from all my clusters — therefore we have a wild card asterisk in the URL just after clusters — clusters/*
  • My first filter, by namespace — namespace%3A%27*migration*
  • The second filter, by cluster name — clusterName%3A%27*vsp*

curl -s ‘searchScope.name=*’ ‘https://<your-sub-name>.tmc.cloud.vmware.com/v1alpha1/clusters/*/objects?sort_by=fullName.clusterName%3Aascending%2CfullName.name%3Aascending&includeTotalCount=true&query=data.kind%3A%5B%27Deployment%27%2C+%27ReplicaSet%27%2C+%27StatefulSet%27%2C+%27DaemonSet%27%5D+AND+%28meta.namespace%3A%27*migration*%27%29+AND+%28fullName.clusterName%3A%27*vsp*%27%29&pagination.size=25&pagination.offset=0' -H “Authorization: Bearer $access_token”

curl -s 'searchScope.name=*' 'https://<your-sub-name>.tmc.cloud.vmware.com/v1alpha1/clusters/*/objects?sort_by=fullName.clusterName%3Aascending%2CfullName.name%3Aascending&includeTotalCount=true&query=data.kind%3A%5B%27Deployment%27%2C+%27ReplicaSet%27%2C+%27StatefulSet%27%2C+%27DaemonSet%27%5D+AND+%28meta.namespace%3A%27*migration*%27%29+AND+%28fullName.clusterName%3A%27*vsp*%27%29&pagination.size=25&pagination.offset=0' -H "Authorization: Bearer $access_token" | jq

{
"objects": [
{
"type": {
"kind": "Object",
"version": "v1alpha1",
"package": "vmware.tanzu.manage.v1alpha1.cluster.object"
},
"fullName": {
"orgId": "<orgid>",
"managementClusterName": "sg-tkg-vsp-mgmt01",
"provisionerName": "default",
"clusterName": "sg-tkg-vsp-demo05",
"name": "k8s-counter-deployment"
},
"meta": {
"uid": "<uid>",
"resourceVersion": "49528682",
"creationTime": "2022-11-16T02:32:14Z",
"labels": {
"app": "k8s-counter"
}
},
"data": {
"kind": "Deployment",
"aggregatedResources": {
"totalPods": 3
},
"namespaceName": "migrationnamespace",
.....snip

The key information I needed from the JSON data is 👇

"clusterName": "sg-tkg-vsp-demo05"
"name": "k8s-counter-deployment"
"kind": "Deployment",
"aggregatedResources": {
"totalPods": 3

Looking at some previous jq filters I came up with this so I can get a consolidated view

| jq '.objects[] | {clusterName: .fullName.clusterName, name: .fullName.name, object: .data.kind, totalPods: .data.aggregatedResources.totalPods}'

So the full command looks like so……

curl -s ‘searchScope.name=*’ ‘https://<your-sub-name>.tmc.cloud.vmware.com/v1alpha1/clusters/*/objects?sort_by=fullName.clusterName%3Aascending%2CfullName.name%3Aascending&includeTotalCount=true&query=data.kind%3A%5B%27Deployment%27%2C+%27ReplicaSet%27%2C+%27StatefulSet%27%2C+%27DaemonSet%27%5D+AND+%28meta.namespace%3A%27*migration*%27%29+AND+%28fullName.clusterName%3A%27*vsp*%27%29&pagination.size=25&pagination.offset=0’ -H “Authorization: Bearer $access_token” | jq ‘.objects[] | {clusterName: .fullName.clusterName, name: .fullName.name, object: .data.kind, totalPods: .data.aggregatedResources.totalPods}’

Giving this — in my on-prem vSphere env. “totalPods”: null as the app is running in AWS.

{
"clusterName": "sg-tkg-vsp-demo05",
"name": "k8s-counter-deployment",
"object": "Deployment",
"totalPods": null
}
{
"clusterName": "sg-tkg-vsp-demo05",
"name": "k8s-counter-deployment-5d48bb8988",
"object": "ReplicaSet",
"totalPods": null
}
{
"clusterName": "sg-tkg-vsp-demo05",
"name": "postgres",
"object": "Deployment",
"totalPods": null
}
{
"clusterName": "sg-tkg-vsp-demo05",
"name": "postgres-5c6d847dd",
"object": "ReplicaSet",
"totalPods": null
}

I then modified the filter for the Cluster, filter=aws showing only my TKG Clusters in my AWS env

  • Name.clusterName%3A%27*aws*

Giving this

{
"clusterName": "sg-tkg-aws-demo2",
"name": "k8s-counter-deployment",
"object": "Deployment",
"totalPods": 3
}
{
"clusterName": "sg-tkg-aws-demo2",
"name": "k8s-counter-deployment-6dbc5d8b78",
"object": "ReplicaSet",
"totalPods": 3
}
{
"clusterName": "sg-tkg-aws-demo2",
"name": "postgres",
"object": "Deployment",
"totalPods": 1
}
{
"clusterName": "sg-tkg-aws-demo2",
"name": "postgres-7957478b7d",
"object": "ReplicaSet",
"totalPods": 1
}
Photo by Giovanna Gomes on Unsplash

And with that, I feel I’m starting to win this battle — Join me for Part II where I’ve won the war/mission —here

Thanks for reading — happy API’ing
Mike

--

--

Mike Carpendale

Platform architect, digital infrastructure advocate, zwifter, coffee addict, hazy pale ale lover