content/en/blog/2019/announcing-istio-client-go/index.md
We are pleased to announce the initial release of the Istio client go repository which enables developers to gain programmatic access to Istio APIs in a Kubernetes environment. The generated Kubernetes informers and client set in this repository makes it easy for developers to create controllers and perform Create, Read, Update and Delete (CRUD) operations for all Istio Custom Resource Definitions (CRDs).
This was a highly requested functionality by many Istio users, as is evident from the feature requests on the clients generated by Aspen Mesh and the Knative project. If you're currently using one of the above mentioned clients, you can easily switch to using Istio client go like this:
{{< text go>}} import ( ...
As the generated client sets are functionally equivalent, switching the imported client libraries should be sufficient in order to consume the newly generated library.
The Istio client go repository follows the same branching strategy as the Istio API repository, as the client repository depends on the API definitions. If you want to use a stable client set, you can use the release branches or tagged versions in the client go repository. Using the client set is very similar to using the Kubernetes client go, here's a quick example of using the client to list all Istio virtual services in the passed namespace:
{{< text go >}} package main
import ( "log" "os"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd"
versionedclient "istio.io/client-go/pkg/clientset/versioned" )
func main() { kubeconfig := os.Getenv("KUBECONFIG") namespace := os.Getenv("NAMESPACE") if len(kubeconfig) == 0 || len(namespace) == 0 { log.Fatalf("Environment variables KUBECONFIG and NAMESPACE need to be set") } restConfig, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { log.Fatalf("Failed to create k8s rest client: %s", err) }
ic, err := versionedclient.NewForConfig(restConfig) if err != nil { log.Fatalf("Failed to create istio client: %s", err) } // Print all VirtualServices vsList, err := ic.NetworkingV1alpha3().VirtualServices(namespace).List(metav1.ListOptions{}) if err != nil { log.Fatalf("Failed to get VirtualService in %s namespace: %s", namespace, err) } for i := range vsList.Items { vs := vsList.Items[i] log.Printf("Index: %d VirtualService Hosts: %+v\n", i, vs.Spec.GetHosts()) } } {{< /text >}}
You can find a more in-depth example [here](https://github.com/istio/client-go/blob/{{< source_branch_name >}}/cmd/example/client.go).
If you're wondering why it took so long or why was it difficult to generate this client set, this section is for you. In Istio, we use protobuf specifications to write APIs which are then converted to Go definitions using the protobuf tool chain. There are three major challenges which you might face if you're trying to generate Kubernetes client set from a protobuf-generated API:
Creating Kubernetes Wrapper Types - Kubernetes client generation
library only works for Go objects which follow the Kubernetes object
specification for e.g. [Authentication Policy Kubernetes Wrappers](https://github.com/istio/client-go/blob/{{< source_branch_name >}}/pkg/apis/authentication/v1alpha1/types.gen.go).
This means for every API which needs programmatic access, you need to create
these wrappers. Additionally, there is a fair amount of boilerplate needed for
every CRD group, version and kind that needs client code generation.
To automate this process, we created a Kubernetes type
generator tool
which can automatically create the Kubernetes types based on annotations.
The annotations parsed by this tool and the various available options
are explained in the README.
Note that if you're using protobuf tools to generate Go types, you would need to
add these annotations as comments in the proto files, so that the comments are
present in the generated Go files which are then used by this tool.
Generating deep copy methods - In Kubernetes client machinery, if you want to
mutate any object returned from the client set, you are required to make a copy
of the object to prevent modifying the object in-place in the cache store. The
canonical way to do this is to create a deepcopy method on all nested types.
We created a tool protoc deep copy
generator
which is a protoc plugin and can automatically create deepcopy method
based on annotations using the Proto library utility Proto
Clone. Here's an
[example](https://github.com/istio/api/blob/{{< source_branch_name >}}/authentication/v1alpha1/policy_deepcopy.gen.go)
of the generated deepcopy method.
Marshaling and Unmarshaling types to/from JSON - For the types generated
from proto definitions, it is often problematic to use the default Go JSON
encoder/decoder as there are various fields like protobuf's oneof which requires
special handling. Additionally, any Proto fields with underscores in their
name might serialize/deserialize to different field names depending on the
encoder/decoder as the Go struct tag are generated
differently.
It is always recommended to use protobuf primitives for
serializing/deserializing to JSON instead of relying on default Go
library. We created a tool protoc JSON
shim which
is a protoc plugin and can automatically create Marshalers/Unmarshalers for
all Go type generated from Proto definitions. Here's an
[example](https://github.com/istio/api/blob/{{< source_branch_name >}}/authentication/v1alpha1/policy_json.gen.go)
of the code generated by this tool.
I'm hoping that the newly released client library enables users to create more integrations and controllers for the Istio APIs, and the tools mentioned above can be used by developers to generate Kubernetes client set from Proto APIs.