func IntoObject()

in pkg/kube/inject/inject.go [606:798]


func IntoObject(injector Injector, sidecarTemplate Templates, valuesConfig ValuesConfig,
	revision string, meshconfig *meshconfig.MeshConfig, in runtime.Object, warningHandler func(string)) (interface{}, error) {
	out := in.DeepCopyObject()

	var deploymentMetadata metav1.ObjectMeta
	var metadata *metav1.ObjectMeta
	var podSpec *corev1.PodSpec
	var typeMeta metav1.TypeMeta

	// Handle Lists
	if list, ok := out.(*corev1.List); ok {
		result := list

		for i, item := range list.Items {
			obj, err := FromRawToObject(item.Raw)
			if runtime.IsNotRegisteredError(err) {
				continue
			}
			if err != nil {
				return nil, err
			}

			r, err := IntoObject(injector, sidecarTemplate, valuesConfig, revision, meshconfig, obj, warningHandler) // nolint: vetshadow
			if err != nil {
				return nil, err
			}

			re := runtime.RawExtension{}
			re.Object = r.(runtime.Object)
			result.Items[i] = re
		}
		return result, nil
	}

	// CronJobs have JobTemplates in them, instead of Templates, so we
	// special case them.
	switch v := out.(type) {
	case *batch.CronJob:
		job := v
		typeMeta = job.TypeMeta
		metadata = &job.Spec.JobTemplate.ObjectMeta
		deploymentMetadata = job.ObjectMeta
		podSpec = &job.Spec.JobTemplate.Spec.Template.Spec
	case *corev1.Pod:
		pod := v
		typeMeta = pod.TypeMeta
		metadata = &pod.ObjectMeta
		deploymentMetadata = pod.ObjectMeta
		podSpec = &pod.Spec
	case *appsv1.Deployment: // Added to be explicit about the most expected case
		deploy := v
		typeMeta = deploy.TypeMeta
		deploymentMetadata = deploy.ObjectMeta
		metadata = &deploy.Spec.Template.ObjectMeta
		podSpec = &deploy.Spec.Template.Spec
	default:
		// `in` is a pointer to an Object. Dereference it.
		outValue := reflect.ValueOf(out).Elem()

		typeMeta = outValue.FieldByName("TypeMeta").Interface().(metav1.TypeMeta)

		deploymentMetadata = outValue.FieldByName("ObjectMeta").Interface().(metav1.ObjectMeta)

		templateValue := outValue.FieldByName("Spec").FieldByName("Template")
		// `Template` is defined as a pointer in some older API
		// definitions, e.g. ReplicationController
		if templateValue.Kind() == reflect.Ptr {
			if templateValue.IsNil() {
				return out, fmt.Errorf("spec.template is required value")
			}
			templateValue = templateValue.Elem()
		}
		metadata = templateValue.FieldByName("ObjectMeta").Addr().Interface().(*metav1.ObjectMeta)
		podSpec = templateValue.FieldByName("Spec").Addr().Interface().(*corev1.PodSpec)
	}

	name := metadata.Name
	if name == "" {
		name = deploymentMetadata.Name
	}
	namespace := metadata.Namespace
	if namespace == "" {
		namespace = deploymentMetadata.Namespace
	}

	var fullName string
	if deploymentMetadata.Namespace != "" {
		fullName = fmt.Sprintf("%s/%s", deploymentMetadata.Namespace, name)
	} else {
		fullName = name
	}

	kind := typeMeta.Kind

	// Skip injection when host networking is enabled. The problem is
	// that the iptable changes are assumed to be within the pod when,
	// in fact, they are changing the routing at the host level. This
	// often results in routing failures within a node which can
	// affect the network provider within the cluster causing
	// additional pod failures.
	if podSpec.HostNetwork {
		warningStr := fmt.Sprintf("===> Skipping injection because %q has host networking enabled\n",
			fullName)
		if kind != "" {
			warningStr = fmt.Sprintf("===> Skipping injection because %s %q has host networking enabled\n",
				kind, fullName)
		}
		warningHandler(warningStr)
		return out, nil
	}

	// skip injection for injected pods
	if len(podSpec.Containers) > 1 {
		_, hasStatus := metadata.Annotations[annotation.SidecarStatus.Name]
		for _, c := range podSpec.Containers {
			if c.Name == ProxyContainerName && hasStatus {
				warningStr := fmt.Sprintf("===> Skipping injection because %q has injected %q sidecar already\n",
					fullName, ProxyContainerName)
				if kind != "" {
					warningStr = fmt.Sprintf("===> Skipping injection because %s %s %q has host networking enabled\n",
						kind, fullName, ProxyContainerName)
				}
				warningHandler(warningStr)
				return out, nil
			}
		}
	}

	pod := &corev1.Pod{
		ObjectMeta: *metadata,
		Spec:       *podSpec,
	}

	var patchBytes []byte
	var err error
	if injector != nil {
		patchBytes, err = injector.Inject(pod, namespace)
	}
	if err != nil {
		return nil, err
	}
	// TODO(Monkeyanator) istioctl injection still applies just the pod annotation since we don't have
	// the ProxyConfig CRs here.
	if pca, f := metadata.GetAnnotations()[annotation.ProxyConfig.Name]; f {
		var merr error
		meshconfig, merr = mesh.ApplyProxyConfig(pca, meshconfig)
		if merr != nil {
			return nil, merr
		}
	}

	if patchBytes == nil {
		if !injectRequired(IgnoredNamespaces.UnsortedList(), &Config{Policy: InjectionPolicyEnabled}, &pod.Spec, pod.ObjectMeta) {
			warningStr := fmt.Sprintf("===> Skipping injection because %q has sidecar injection disabled\n", fullName)
			if kind != "" {
				warningStr = fmt.Sprintf("===> Skipping injection because %s %q has sidecar injection disabled\n",
					kind, fullName)
			}
			warningHandler(warningStr)
			return out, nil
		}
		params := InjectionParameters{
			pod:        pod,
			deployMeta: deploymentMetadata,
			typeMeta:   typeMeta,
			// Todo replace with some template resolver abstraction
			templates:           sidecarTemplate,
			defaultTemplate:     []string{SidecarTemplateName},
			meshConfig:          meshconfig,
			proxyConfig:         meshconfig.GetDefaultConfig(),
			valuesConfig:        valuesConfig,
			revision:            revision,
			proxyEnvs:           map[string]string{},
			injectedAnnotations: nil,
		}
		patchBytes, err = injectPod(params)
	}
	if err != nil {
		return nil, err
	}
	patched, err := applyJSONPatchToPod(pod, patchBytes)
	if err != nil {
		return nil, err
	}
	patchedObject, _, err := jsonSerializer.Decode(patched, nil, &corev1.Pod{})
	if err != nil {
		return nil, err
	}
	patchedPod := patchedObject.(*corev1.Pod)
	*metadata = patchedPod.ObjectMeta
	*podSpec = patchedPod.Spec
	return out, nil
}