Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package kubernetes
- import (
- "context"
- "encoding/base64"
- "fmt"
- container "cloud.google.com/go/container/apiv1"
- "github.com/pkg/errors"
- "golang.org/x/oauth2"
- containerpb "google.golang.org/genproto/googleapis/container/v1"
- v1core "k8s.io/api/core/v1"
- kerrors "k8s.io/apimachinery/pkg/api/errors"
- v1meta "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/fields"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/jsonmergepatch"
- "k8s.io/apimachinery/pkg/util/mergepatch"
- "k8s.io/client-go/discovery"
- "k8s.io/client-go/dynamic"
- "k8s.io/client-go/kubernetes"
- _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
- "k8s.io/client-go/rest"
- "k8s.io/client-go/tools/clientcmd/api"
- "gitlab.messagebird.io/infrastructure-team/tools/tron/pkg/appenv"
- )
- type Clusters struct {
- Clusters map[string]appenv.ClusterID
- ClusterManager *container.ClusterManagerClient
- TokenSource oauth2.TokenSource
- }
- func (kCls Clusters) GetClientForEnvironment(env string) (*Client, error) {
- clID, ok := kCls.Clusters[env]
- if !ok {
- return nil, errors.New(fmt.Sprintf("no cluster for environment %s", env))
- }
- cluster, err := kCls.ClusterManager.GetCluster(context.TODO(), &containerpb.GetClusterRequest{
- Name: clID.GCloudName(),
- })
- if err != nil {
- return nil, errors.Wrapf(err, "can't get cluster info for %s", env)
- }
- return &Client{
- cluster: cluster,
- tokenSource: kCls.TokenSource,
- ClusterID: clID,
- }, nil
- }
- type Client struct {
- cluster *containerpb.Cluster
- tokenSource oauth2.TokenSource
- ClusterID appenv.ClusterID
- }
- type authorized struct {
- clientset *kubernetes.Clientset
- dynamicClient dynamic.Interface
- discoveryClient resourceDiscovery
- }
- func (c Client) newAuthorized() (*authorized, error) {
- config, err := c.getConfig()
- if err != nil {
- return nil, errors.WithStack(err)
- }
- clientset, err := kubernetes.NewForConfig(config)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- dynClient, err := dynamic.NewForConfig(config)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- discClient, err := newResourceDiscovery(config)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- return &authorized{
- clientset: clientset,
- dynamicClient: dynClient,
- discoveryClient: discClient,
- }, nil
- }
- func (c Client) getConfig() (*rest.Config, error) {
- token, err := c.tokenSource.Token()
- if err != nil {
- return nil, errors.Wrapf(err, "can't issue authentication token")
- }
- caDecoded, err := base64.StdEncoding.DecodeString(c.cluster.MasterAuth.ClusterCaCertificate)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid cluster CA for %s", c.cluster.Name)
- }
- return &rest.Config{
- Host: c.cluster.Endpoint,
- TLSClientConfig: rest.TLSClientConfig{
- CAData: caDecoded,
- },
- AuthProvider: &api.AuthProviderConfig{
- Name: "gcp",
- Config: map[string]string{
- "token": token.AccessToken,
- },
- },
- }, nil
- }
- type groupVersionKind struct {
- group string
- version string
- kind string
- }
- type resourceDiscovery struct {
- gvkToResource map[groupVersionKind]string
- resourceLists []*v1meta.APIResourceList
- }
- func newResourceDiscovery(config *rest.Config) (resourceDiscovery, error) {
- d := resourceDiscovery{
- gvkToResource: map[groupVersionKind]string{},
- }
- client, err := discovery.NewDiscoveryClientForConfig(config)
- if err != nil {
- return d, errors.WithStack(err)
- }
- d.resourceLists, err = client.ServerPreferredResources()
- if err != nil {
- return d, errors.WithStack(err)
- }
- for _, resourceList := range d.resourceLists {
- for _, r := range resourceList.APIResources {
- gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
- if err != nil {
- return d, errors.WithStack(err)
- }
- k := groupVersionKind{
- group: gv.Group,
- version: gv.Version,
- kind: r.Kind,
- }
- d.gvkToResource[k] = r.Name
- }
- }
- return d, nil
- }
- func (d resourceDiscovery) resourceForObject(o runtime.Object) (string, error) {
- gvk := o.GetObjectKind().GroupVersionKind()
- resourceName, ok := d.gvkToResource[groupVersionKind{
- group: gvk.Group,
- version: gvk.Version,
- kind: gvk.Kind,
- }]
- if !ok {
- return "", errors.Errorf("can't find resource for %s/%s/%s", gvk.Version, gvk.Group, gvk.Kind)
- }
- return resourceName, nil
- }
- func (c Client) Apply(manifest Manifest) error {
- auth, err := c.newAuthorized()
- if err != nil {
- return errors.WithStack(err)
- }
- for _, resource := range manifest.resources {
- gvk := resource.GetObjectKind().GroupVersionKind()
- resourceName, err := auth.discoveryClient.resourceForObject(resource)
- if err != nil {
- return errors.WithStack(err)
- }
- namespaceableResource := auth.dynamicClient.Resource(schema.GroupVersionResource{
- Group: gvk.Group,
- Version: gvk.Version,
- Resource: resourceName,
- })
- var (
- r dynamic.ResourceInterface
- unstruct unstructured.Unstructured
- )
- if resource.GetNamespace() == "" {
- r = namespaceableResource
- } else {
- r = namespaceableResource.Namespace(resource.GetNamespace())
- }
- if unstruct.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(resource); err != nil {
- return errors.WithStack(err)
- }
- annotations := unstruct.GetAnnotations()
- if annotations == nil {
- annotations = map[string]string{}
- }
- oldConfiguration := []byte(annotations[v1core.LastAppliedConfigAnnotation])
- newConfiguration, err := unstruct.MarshalJSON()
- if err != nil {
- return errors.WithStack(err)
- }
- annotations[v1core.LastAppliedConfigAnnotation] = string(newConfiguration)
- unstruct.SetAnnotations(annotations)
- if existing, err := r.Get(resource.GetName(), v1meta.GetOptions{}); err != nil && !kerrors.IsNotFound(err) {
- return errors.WithStack(err)
- } else if kerrors.IsNotFound(err) {
- if _, err := r.Create(&unstruct, v1meta.CreateOptions{}); err != nil {
- return errors.Wrapf(err, "resource %s/%s/%s %s/%s", gvk.Version, gvk.Group, gvk.Kind, resource.GetNamespace(), resource.GetName())
- }
- } else {
- preconditions := []mergepatch.PreconditionFunc{
- mergepatch.RequireKeyUnchanged("apiVersion"),
- mergepatch.RequireKeyUnchanged("kind"),
- mergepatch.RequireMetadataKeyUnchanged("name"),
- }
- original, err := existing.MarshalJSON()
- if err != nil {
- return errors.WithStack(err)
- }
- patch, err := jsonmergepatch.CreateThreeWayJSONMergePatch(oldConfiguration, newConfiguration, original, preconditions...)
- if err != nil {
- return errors.WithStack(err)
- }
- if _, err := r.Patch(resource.GetName(), types.MergePatchType, patch, v1meta.PatchOptions{}); err != nil {
- return errors.Wrapf(err, "resource %s/%s/%s %s/%s", gvk.Version, gvk.Group, gvk.Kind, resource.GetNamespace(), resource.GetName())
- }
- }
- }
- return nil
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement