github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/runtime/kubernetes/util.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/runtime/kubernetes/util.go
    14  
    15  package kubernetes
    16  
    17  import (
    18  	"encoding/base64"
    19  	"encoding/json"
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/tickoalcantara12/micro/v3/service/logger"
    24  	"github.com/tickoalcantara12/micro/v3/service/runtime"
    25  	"github.com/tickoalcantara12/micro/v3/service/runtime/kubernetes/api"
    26  	"github.com/tickoalcantara12/micro/v3/service/runtime/kubernetes/client"
    27  )
    28  
    29  // getServices queries kubernetes for services. It gets information from both the pods and the
    30  // deployments
    31  func (k *kubernetes) getServices(opts ...client.GetOption) ([]*runtime.Service, error) {
    32  	// get the deployments
    33  	depList := new(client.DeploymentList)
    34  	d := &client.Resource{
    35  		Kind:  "deployment",
    36  		Value: depList,
    37  	}
    38  	if err := k.client.Get(d, opts...); err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	srvMap := make(map[string]*runtime.Service, len(depList.Items))
    43  
    44  	// loop through the services and create a deployment for each
    45  	for _, kdep := range depList.Items {
    46  		srv := &runtime.Service{
    47  			Name:     kdep.Metadata.Labels["name"],
    48  			Version:  kdep.Metadata.Labels["version"],
    49  			Source:   kdep.Metadata.Labels["source"],
    50  			Metadata: kdep.Metadata.Annotations,
    51  		}
    52  
    53  		// this metadata was injected by the k8s runtime
    54  		delete(srv.Metadata, "name")
    55  		delete(srv.Metadata, "version")
    56  		delete(srv.Metadata, "source")
    57  
    58  		// parse out deployment status and inject into service metadata
    59  		if len(kdep.Status.Conditions) > 0 {
    60  			srv.Status = transformStatus(kdep.Status.Conditions[0].Type)
    61  			srv.Metadata["started"] = kdep.Status.Conditions[0].LastUpdateTime
    62  		} else {
    63  			srv.Status = runtime.Unknown
    64  		}
    65  
    66  		srvMap[resourceName(srv)] = srv
    67  	}
    68  
    69  	// get the pods from k8s
    70  	podList := new(client.PodList)
    71  	p := &client.Resource{
    72  		Kind:  "pod",
    73  		Value: podList,
    74  	}
    75  	if err := k.client.Get(p, opts...); err != nil {
    76  		logger.Errorf("Error fetching pods: %v", err)
    77  		return nil, nil
    78  	}
    79  
    80  	for _, item := range podList.Items {
    81  		// skip if we can't get the container
    82  		if len(item.Status.Containers) == 0 {
    83  			continue
    84  		}
    85  
    86  		// lookup the service in the map
    87  		key := resourceName(&runtime.Service{
    88  			Name:    item.Metadata.Labels["name"],
    89  			Version: item.Metadata.Labels["version"],
    90  		})
    91  		srv, ok := srvMap[key]
    92  		if !ok {
    93  			continue
    94  		}
    95  
    96  		// use the pod status over the deployment status (contains more details)
    97  		srv.Status = transformStatus(item.Status.Phase)
    98  
    99  		// set start time
   100  		state := item.Status.Containers[0].State
   101  		if state.Running != nil {
   102  			srv.Metadata["started"] = state.Running.Started
   103  		}
   104  
   105  		// set status from waiting
   106  		if v := state.Waiting; v != nil {
   107  			srv.Status = runtime.Pending
   108  		}
   109  	}
   110  
   111  	// turn the map into an array
   112  	services := make([]*runtime.Service, 0, len(srvMap))
   113  	for _, srv := range srvMap {
   114  		services = append(services, srv)
   115  	}
   116  	return services, nil
   117  }
   118  
   119  func (k *kubernetes) createCredentials(service *runtime.Service, options *runtime.CreateOptions) error {
   120  	if len(options.Secrets) == 0 {
   121  		return nil
   122  	}
   123  
   124  	data := make(map[string]string, len(options.Secrets))
   125  	for key, value := range options.Secrets {
   126  		data[key] = base64.StdEncoding.EncodeToString([]byte(value))
   127  	}
   128  
   129  	// construct the k8s secret object
   130  	secret := &client.Secret{
   131  		Type: "Opaque",
   132  		Data: data,
   133  		Metadata: &client.Metadata{
   134  			Name:      resourceName(service),
   135  			Namespace: options.Namespace,
   136  		},
   137  	}
   138  
   139  	// crete the secret in kubernetes
   140  	err := k.client.Create(&client.Resource{
   141  		Kind:  "secret",
   142  		Name:  resourceName(service),
   143  		Value: secret,
   144  	}, client.CreateNamespace(options.Namespace))
   145  
   146  	// ignore the error if the creds already exist
   147  	if err == nil || parseError(err).Reason == "AlreadyExists" {
   148  		return nil
   149  	}
   150  
   151  	if logger.V(logger.WarnLevel, logger.DefaultLogger) {
   152  		logger.Warnf("Error generating auth credentials for service: %v", err)
   153  	}
   154  	return err
   155  }
   156  
   157  func (k *kubernetes) deleteCredentials(service *runtime.Service, options *runtime.CreateOptions) error {
   158  	// construct the k8s secret object
   159  	secret := &client.Secret{
   160  		Type: "Opaque",
   161  		Metadata: &client.Metadata{
   162  			Name:      resourceName(service),
   163  			Namespace: options.Namespace,
   164  		},
   165  	}
   166  
   167  	// crete the secret in kubernetes
   168  	err := k.client.Delete(&client.Resource{
   169  		Kind:  "secret",
   170  		Name:  resourceName(service),
   171  		Value: secret,
   172  	}, client.DeleteNamespace(options.Namespace))
   173  
   174  	if err != nil && logger.V(logger.WarnLevel, logger.DefaultLogger) {
   175  		logger.Warnf("Error deleting auth credentials for service: %v", err)
   176  	}
   177  
   178  	return err
   179  }
   180  
   181  func resourceName(srv *runtime.Service) string {
   182  	return fmt.Sprintf("%v-%v", client.Format(srv.Name), client.Format(srv.Version))
   183  }
   184  
   185  // transformStatus takes a deployment status (deploymentcondition.type) and transforms it into a
   186  // runtime service status, e.g. containercreating => starting
   187  func transformStatus(depStatus string) runtime.ServiceStatus {
   188  	switch strings.ToLower(depStatus) {
   189  	case "pending":
   190  		return runtime.Pending
   191  	case "containercreating":
   192  		return runtime.Starting
   193  	case "imagepullbackoff":
   194  		return runtime.Error
   195  	case "crashloopbackoff":
   196  		return runtime.Error
   197  	case "error":
   198  		return runtime.Error
   199  	case "running":
   200  		return runtime.Running
   201  	case "available":
   202  		return runtime.Running
   203  	case "succeeded":
   204  		return runtime.Stopped
   205  	case "failed":
   206  		return runtime.Error
   207  	case "waiting":
   208  		return runtime.Pending
   209  	case "terminated":
   210  		return runtime.Stopped
   211  	default:
   212  		return runtime.Unknown
   213  	}
   214  }
   215  
   216  func parseError(err error) *api.Status {
   217  	status := new(api.Status)
   218  	json.Unmarshal([]byte(err.Error()), &status)
   219  	return status
   220  }