<p>I'm trying to work with Istio from Go, and are using Kubernetes and Istio go-client code.</p>

<p>The problem I'm having is that I can't specify <code>ObjectMeta</code> or <code>TypeMeta</code> in my Istio-<code>ServiceRole</code> object. I can only specify <code>rules</code>, which are inside the <code>spec</code>.</p>

<p>Below you can see what I got working: </p>

<pre class="lang-golang prettyprint-override"><code>import (
    v1alpha1 "istio.io/api/rbac/v1alpha1"
)


func getDefaultServiceRole(app nais.Application) *v1alpha1.ServiceRole {
    return &amp;v1alpha1.ServiceRole{
        Rules: []*v1alpha1.AccessRule{
            {
                Ports: []int32{2},
            },
        },
    }
}
</code></pre>

<p>What I would like to do is have this code work:</p>

<pre class="lang-golang prettyprint-override"><code>func getDefaultServiceRole(app *nais.Application) *v1alpha1.ServiceRole {
    return &amp;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},
                },
            },
        },
    },
}
</code></pre>

<p>Can anyone point me in the right direction?</p>

<p>Ah - this is a pretty painful point: Istio requires Kubernetes CRD wrapper metadata (primarily the <code>name</code> and <code>namespace</code> 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 - <a href="https://github.com/istio/api/blob/master/mcp/v1alpha1/metadata.proto" rel="nofollow noreferrer">does encode these fields as protobufs</a> but that doesn't help for your use case.) Instead, you should use the types in <a href="https://godoc.org/istio.io/istio/pilot/pkg/config/kube/crd" rel="nofollow noreferrer"><code>istio.io/istio/pilot/pkg/config/kube/crd</code></a>, which implement the K8s CRD interface.</p>

<p>The easiest way to work with the Istio objects in golang is to use Pilot's libraries, particularly the <a href="https://godoc.org/istio.io/istio/pilot/pkg/model" rel="nofollow noreferrer"><code>istio.io/istio/pilot/pkg/model</code></a> and <a href="https://godoc.org/istio.io/istio/pilot/pkg/config/kube/crd" rel="nofollow noreferrer"><code>istio.io/istio/pilot/pkg/config/kube/crd</code></a> packages, as well as the <a href="https://godoc.org/istio.io/istio/pilot/pkg/model#Config" rel="nofollow noreferrer"><code>model.Config</code></a> struct. You can either pass around the full <code>model.Config</code> (not great because <code>spec</code> has type <code>proto.Message</code> so you need type assertions to extract the data you care about), or pass around the inner object wrap it in a <code>model.Config</code> before you push it. You can use the <a href="https://godoc.org/istio.io/istio/pilot/pkg/model#ProtoSchema" rel="nofollow noreferrer"><code>model.ProtoSchema</code></a> type to help with conversion to and from YAML and JSON. <a href="https://godoc.org/istio.io/istio/pilot/pkg/model#pkg-variables" rel="nofollow noreferrer">Pilot only defines <code>ProtoSchema</code> objects for the networking API</a>, the type is public and you can create them for arbitrary types.</p>

<p>So, using your example code I might try something like:</p>

<pre class="lang-golang prettyprint-override"><code>import (
    v1alpha1 "istio.io/api/rbac/v1alpha1"
   "istio.io/istio/pilot/pkg/model"
)


func getDefaultServiceRole() *v1alpha1.ServiceRole {
    return &amp;v1alpha1.ServiceRole{
        Rules: []*v1alpha1.AccessRule{
            {
                Ports: []int32{2},
            },
        },
    }
}

func toConfig(app *nais.Application, role *v1alpha1.ServiceRole) model.Config {
    return &amp;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
}
</code></pre>

<p>As a more complete example, we built the Istio CloudMap operator in this style. <a href="https://github.com/tetratelabs/istio-cloud-map/blob/master/pkg/control/synchronizer.go#L64-L91" rel="nofollow noreferrer">Here's the core of it that pushes config to K8s with Pilot libraries.</a> Here's <a href="https://github.com/tetratelabs/istio-cloud-map/blob/e3c95237f5e0743f9ec9898fdf1809321df91b5e/pkg/control/synchronizer.go#L28" rel="nofollow noreferrer">the incantation to create an instance of model.ConfigStore to use to create objects</a>. Finally, I want to call out explicitly as it's only implicit in the example: when you call <code>Create</code> on the <code>model.ConfigStore</code>, the <code>ConfigStore</code> relies on the metadata in the <code>ProtoSchema</code> objects used to create it. So be sure to initialize the store with <code>ProtoSchema</code> objects for all of the types you'll be working with.</p>

<hr>

<p>You can achieve the same using just the K8s client libraries and the <code>istio.io/istio/pilot/pkg/config/kube/crd</code> package, but I have not done it firsthand and don't have examples handy.</p>
