Skip to content

Latest commit

 

History

History
1291 lines (1154 loc) · 56.5 KB

CoreV1PodTemplate.org

File metadata and controls

1291 lines (1154 loc) · 56.5 KB

Test Writing Flow

[91%] Cluster Setup

Deploy APISnoop

kubectl apply -f ~/cncf/apisnoop/deployment/k8s/raiinbow.yaml

Identify an untested feature Using APISnoop

We can query a few tables, this is a simple core api query for get APIs that do not hit volumes.

SELECT
  operation_id,
  k8s_action,
  path,
  description
  FROM untested_stable_core_endpoints
  where path not like '%volume%'
  and operation_id like '%PodTemplate%'
 ORDER BY operation_id desc
 LIMIT 25
       ;
                operation_id                 |    k8s_action    |                        path                        |                description                 
---------------------------------------------+------------------+----------------------------------------------------+--------------------------------------------
 replaceCoreV1NamespacedPodTemplate          | put              | /api/v1/namespaces/{namespace}/podtemplates/{name} | replace the specified PodTemplate
 readCoreV1NamespacedPodTemplate             | get              | /api/v1/namespaces/{namespace}/podtemplates/{name} | read the specified PodTemplate
 patchCoreV1NamespacedPodTemplate            | patch            | /api/v1/namespaces/{namespace}/podtemplates/{name} | partially update the specified PodTemplate
 listCoreV1PodTemplateForAllNamespaces       | list             | /api/v1/podtemplates                               | list or watch objects of kind PodTemplate
 deleteCoreV1CollectionNamespacedPodTemplate | deletecollection | /api/v1/namespaces/{namespace}/podtemplates        | delete collection of PodTemplate
(5 rows)

You can iterate over a query until you have a set of endpoints you’d like to write a test for, usually by adjusting the columns you view or by extending the where clause to filter more specifically.

Use API Reference to Lightly Document the Feature

Finding documentation for this API

The mock test

This is where the test code goes. It is useful to seperate it into blocks which can be evaluted independently.

You can write tests in a variety of languages, outlined in “Client Libraries” on k8s reference page.

Whichever language you choose, you want to make sure to set the useragent to something starting with live-test. This ensures apisnoop’s queries around testing work correctly.

We’ve included sample tests in Go and Javascript.

Example in Go

go get -v -u k8s.io/apimachinery/pkg/apis/meta/v1
go get -v -u k8s.io/client-go/kubernetes
go get -v -u k8s.io/client-go/tools/clientcmd
package main

import (
  "fmt"
  "flag"
  "os"
  //"encoding/json"
  v1 "k8s.io/api/core/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
  "k8s.io/apimachinery/pkg/types"
)

func main() {
  // uses the current context in kubeconfig
  kubeconfig := flag.String("kubeconfig", fmt.Sprintf("%v/%v/%v", os.Getenv("HOME"), ".kube", "config"), "(optional) absolute path to the kubeconfig file")
     flag.Parse()
  config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
  if err != nil {
    fmt.Println(err)
    return
  }
  // make our work easier to find in the audit_event queries
  config.UserAgent = "live-test-writing"
  // creates the clientset
  clientset, _ := kubernetes.NewForConfig(config)

  podTemplateName := "nginx-pod-template"

  // get a list of PodTemplates
  podTemplateList, err := clientset.CoreV1().PodTemplates("").List(metav1.ListOptions{})
  if err != nil {
    fmt.Println("[error]", err)
    return
  }
  if len(podTemplateList.Items) > 0 {
    fmt.Println("[error] templates should not be populated")
    return
  }

  fmt.Println("[status] no PodTemplates found")

  // create a PodTemplate
  _, err = clientset.CoreV1().PodTemplates("default").Create(&v1.PodTemplate{
    ObjectMeta: metav1.ObjectMeta{
      Name: podTemplateName,
    },
    Template: v1.PodTemplateSpec{
      Spec: v1.PodSpec{
        Containers: []v1.Container{
          {Name: "nginx", Image: "nginx"},
        },
      },
    },
  })
  if err != nil {
    fmt.Println("[error]", err)
    return
  }

  fmt.Println("[status] PodTemplate created")

  // get template
  podTemplateRead, err := clientset.CoreV1().PodTemplates("default").Get(podTemplateName, metav1.GetOptions{})
  if err != nil {
    fmt.Println("[error]", err)
    return
  }
  if podTemplateRead.ObjectMeta.Name != podTemplateName {
    fmt.Println("[error] PodTemplate name doesn't match")
    return
  }

  fmt.Println("[status] found created PodTemplate")

  // patch template
  PodTemplatePatch := fmt.Sprintf(`{"metadata":{"labels":{"a":"1"}}}`)
  _, err = clientset.CoreV1().PodTemplates("default").Patch(podTemplateName, types.StrategicMergePatchType, []byte(PodTemplatePatch))
  if err != nil {
    fmt.Println("[error]", err)
    return
  }

  fmt.Println("[status] patched PodTemplate with label a=1")

  // get template (ensure label is there)
  podTemplateRead, err = clientset.CoreV1().PodTemplates("default").Get(podTemplateName, metav1.GetOptions{})
  if err != nil {
    fmt.Println("[error]", err)
    return
  }
  if podTemplateRead.ObjectMeta.Labels["a"] != "1" {
    fmt.Println("[error] template doesn't contain the label a=1")
    return
  }

  fmt.Println("[status] found label on PodTemplate")

  // list PodTemplates on all namespaces by label a=1
  podTemplateListWithLabel, err := clientset.CoreV1().PodTemplates("").List(metav1.ListOptions{
    LabelSelector: "a=1",
  })
  if err != nil {
    fmt.Println("[error]", err)
    return
  }
  foundPodTemplateWithLabel := false
  for _, podTemplate := range podTemplateListWithLabel.Items {
    if podTemplate.ObjectMeta.Name == podTemplateName && podTemplate.ObjectMeta.Labels["a"] == "1" {
      foundPodTemplateWithLabel = true
    }
  }
  if foundPodTemplateWithLabel == false || len(podTemplateListWithLabel.Items) == 0 {
    fmt.Println("[error] PodTemplate doesn't contain the label")
    return
  }

  fmt.Println("[status] found PodTemplate by label")

  err = clientset.CoreV1().PodTemplates("default").Delete(podTemplateName, &metav1.DeleteOptions{})
  if err != nil {
    fmt.Println("[error]", err)
    return
  }

  fmt.Println("[status] deleted PodTemplate")

  podTemplateListWithLabel, err = clientset.CoreV1().PodTemplates("").List(metav1.ListOptions{
    LabelSelector: "a=1",
  })
  if err != nil {
    fmt.Println("[error]", err)
    return
  }
  if len(podTemplateListWithLabel.Items) > 0 {
    fmt.Println("[error] list returned a PodTemplate matching the requested labels")
    return
  }

  fmt.Println("[status] no PodTemplates found")

  fmt.Println("[status] complete")
}

Verify with APISnoop

select distinct useragent from audit_event where bucket='apisnoop' and useragent not like 'kube%' and useragent not like 'coredns%' and useragent not like 'kindnetd%';
-- select * from endpoints_hit_by_new_test where useragent like 'Swagger%' or useragent like 'live-%';
--select * from endpoints_hit_by_new_test where useragent like 'Swagger%';
-- select * from endpoints_hit_by_new_test where useragent like 'live%';
     useragent     
-------------------
 live-test-writing
(1 row)

NOTE: for the projected change in coverage, your test functions must be configured with a useragent that starts with live-test, otherwise endpoints hit by that test won’t be counted as part of new coverage.

select * from endpoints_hit_by_new_test where useragent like 'live%';

select * from projected_change_in_coverage;
   category    | total_endpoints | old_coverage | new_coverage | change_in_number 
---------------+-----------------+--------------+--------------+------------------
 test_coverage |             438 |          183 |          186 |                3
(1 row)

Footnotes

for aaron

select kind, sub_kind, field_path, test_hits, distance from full_podspec_field_coverage where job != 'live';
[[file:results.txt]]