I'm trying to work with Istio from Go, and are using Kubernetes and Istio go-client code.
The problem I'm having is that I can't specify ObjectMeta
or TypeMeta
in my Istio-ServiceRole
object. I can only specify rules
, which are inside the spec
.
Below you can see what I got working:
import (
v1alpha1 "istio.io/api/rbac/v1alpha1"
)
func getDefaultServiceRole(app nais.Application) *v1alpha1.ServiceRole {
return &v1alpha1.ServiceRole{
Rules: []*v1alpha1.AccessRule{
{
Ports: []int32{2},
},
},
}
}
What I would like to do is have this code work:
func getDefaultServiceRole(app *nais.Application) *v1alpha1.ServiceRole {
return &v1alpha1.ServiceRole{
TypeMeta: metav1.TypeMeta{
Kind: "ServiceRole",
APIVersion: "v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: app.Name,
Namespace: app.Namespace,
},
Spec: v1alpha1.ServiceRole{
Rules: []*v1alpha1.AccessRule{
{
Ports: []int32{2},
},
},
},
},
}
Can anyone point me in the right direction?
Ah - this is a pretty painful point: Istio requires Kubernetes CRD wrapper metadata (primarily the name
and namespace
fields), but those fields are not part of the API objects themselves nor are they represented in the protos. (This is changing with the new MCP API for configuring components - which Galley uses - does encode these fields as protobufs but that doesn't help for your use case.) Instead, you should use the types in istio.io/istio/pilot/pkg/config/kube/crd
, which implement the K8s CRD interface.
The easiest way to work with the Istio objects in golang is to use Pilot's libraries, particularly the istio.io/istio/pilot/pkg/model
and istio.io/istio/pilot/pkg/config/kube/crd
packages, as well as the model.Config
struct. You can either pass around the full model.Config
(not great because spec
has type proto.Message
so you need type assertions to extract the data you care about), or pass around the inner object wrap it in a model.Config
before you push it. You can use the model.ProtoSchema
type to help with conversion to and from YAML and JSON. Pilot only defines ProtoSchema
objects for the networking API, the type is public and you can create them for arbitrary types.
So, using your example code I might try something like:
import (
v1alpha1 "istio.io/api/rbac/v1alpha1"
"istio.io/istio/pilot/pkg/model"
)
func getDefaultServiceRole() *v1alpha1.ServiceRole {
return &v1alpha1.ServiceRole{
Rules: []*v1alpha1.AccessRule{
{
Ports: []int32{2},
},
},
}
}
func toConfig(app *nais.Application, role *v1alpha1.ServiceRole) model.Config {
return &model.Config{
ConfigMeta: model.ConfigMeta{
Name: app.Name,
Namespace: app.Namespace,
},
Spec: app,
}
}
type Client model.ConfigStore
func (c Client) CreateRoleFor(app nais.Application, role *v1alpha1.ServiceRole) error {
cfg := toConfig(app, role)
_, err := c.Create(cfg)
return err
}
As a more complete example, we built the Istio CloudMap operator in this style. Here's the core of it that pushes config to K8s with Pilot libraries. Here's the incantation to create an instance of model.ConfigStore to use to create objects. Finally, I want to call out explicitly as it's only implicit in the example: when you call Create
on the model.ConfigStore
, the ConfigStore
relies on the metadata in the ProtoSchema
objects used to create it. So be sure to initialize the store with ProtoSchema
objects for all of the types you'll be working with.
You can achieve the same using just the K8s client libraries and the istio.io/istio/pilot/pkg/config/kube/crd
package, but I have not done it firsthand and don't have examples handy.