Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to add/remove a specific port/env var of a specific container #131

Merged
merged 6 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 19 additions & 197 deletions pkg/devfile/parser/configurables.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package parser

import (
"fmt"
"strconv"
"strings"

v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/pkg/devfile/parser/data/v2/common"
corev1 "k8s.io/api/core/v1"
)

const (
Expand All @@ -27,70 +22,46 @@ func (d DevfileObj) SetMetadataName(name string) error {
return d.WriteYamlDevfile()
}

// AddEnvVars adds environment variables to all the components in a devfile
func (d DevfileObj) AddEnvVars(otherList []v1.EnvVar) error {
components, err := d.Data.GetComponents(common.DevfileOptions{})
// AddEnvVars accepts a map of container name mapped to an array of the env vars to be set;
// it adds the envirnoment variables to a given container name, and writes to the devfile
// Example of containerEnvMap : {"runtime": {{Name: "Foo", Value: "Bar"}}}
func (d DevfileObj) AddEnvVars(containerEnvMap map[string][]v1.EnvVar) error {
err := d.Data.AddEnvVars(containerEnvMap)
if err != nil {
return err
}
for _, component := range components {
if component.Container != nil {
component.Container.Env = Merge(component.Container.Env, otherList)
d.Data.UpdateComponent(component)
}
}
return d.WriteYamlDevfile()
}

// RemoveEnvVars removes the environment variables which have the keys from all the components in a devfile
func (d DevfileObj) RemoveEnvVars(keys []string) (err error) {
components, err := d.Data.GetComponents(common.DevfileOptions{})
// RemoveEnvVars accepts a map of container name mapped to an array of environment variables to be removed;
// it removes the env vars from the specified container name and writes it to the devfile
func (d DevfileObj) RemoveEnvVars(containerEnvMap map[string][]string) (err error) {
err = d.Data.RemoveEnvVars(containerEnvMap)
if err != nil {
return err
}
for _, component := range components {
if component.Container != nil {
component.Container.Env, err = RemoveEnvVarsFromList(component.Container.Env, keys)
if err != nil {
return err
}
d.Data.UpdateComponent(component)
}
}
return d.WriteYamlDevfile()
}

// SetPorts converts ports to endpoints, adds to a devfile
func (d DevfileObj) SetPorts(ports ...string) error {
components, err := d.Data.GetComponents(common.DevfileOptions{})
// SetPorts accepts a map of container name mapped to an array of port numbers to be set;
// it converts ports to endpoints, sets the endpoint to a given container name, and writes to the devfile
// Example of containerPortsMap: {"runtime": {"8080", "9000"}, "wildfly": {"12956"}}
func (d DevfileObj) SetPorts(containerPortsMap map[string][]string) error {
err := d.Data.SetPorts(containerPortsMap)
if err != nil {
return err
}
endpoints, err := portsToEndpoints(ports...)
if err != nil {
return err
}
for _, component := range components {
if component.Container != nil {
component.Container.Endpoints = addEndpoints(component.Container.Endpoints, endpoints)
d.Data.UpdateComponent(component)
}
}
return d.WriteYamlDevfile()
}

// RemovePorts removes all container endpoints from a devfile
func (d DevfileObj) RemovePorts() error {
components, err := d.Data.GetComponents(common.DevfileOptions{})
// RemovePorts accepts a map of container name mapped to an array of port numbers to be removed;
// it removes the container endpoints with the specified port numbers of the specified container, and writes to the devfile
// Example of containerPortsMap: {"runtime": {"8080", "9000"}, "wildfly": {"12956"}}
func (d DevfileObj) RemovePorts(containerPortsMap map[string][]string) error {
err := d.Data.RemovePorts(containerPortsMap)
if err != nil {
return err
}
for _, component := range components {
if component.Container != nil {
component.Container.Endpoints = []v1.Endpoint{}
d.Data.UpdateComponent(component)
}
}
return d.WriteYamlDevfile()
}

Expand Down Expand Up @@ -146,152 +117,3 @@ func (d DevfileObj) GetMemory() string {
func (d DevfileObj) GetMetadataName() string {
return d.Data.GetMetadata().Name
}

func portsToEndpoints(ports ...string) ([]v1.Endpoint, error) {
var endpoints []v1.Endpoint
conPorts, err := GetContainerPortsFromStrings(ports)
if err != nil {
return nil, err
}
for _, port := range conPorts {

endpoint := v1.Endpoint{
Name: fmt.Sprintf("port-%d-%s", port.ContainerPort, strings.ToLower(string(port.Protocol))),
TargetPort: int(port.ContainerPort),
Protocol: v1.EndpointProtocol(strings.ToLower(string(port.Protocol))),
}
endpoints = append(endpoints, endpoint)
}
return endpoints, nil

}

func addEndpoints(current []v1.Endpoint, other []v1.Endpoint) []v1.Endpoint {
newList := make([]v1.Endpoint, len(current))
copy(newList, current)
for _, ep := range other {
present := false

for _, presentep := range newList {

protocol := presentep.Protocol
if protocol == "" {
// endpoint protocol default value is http
protocol = "http"
}
// if the target port and protocol match, we add a case where the protocol is not provided and hence we assume that to be "tcp"
if presentep.TargetPort == ep.TargetPort && (ep.Protocol == protocol) {
present = true
break
}
}
if !present {
newList = append(newList, ep)
}
}

return newList
}

// GetContainerPortsFromStrings generates ContainerPort values from the array of string port values
// ports is the array containing the string port values
func GetContainerPortsFromStrings(ports []string) ([]corev1.ContainerPort, error) {
var containerPorts []corev1.ContainerPort
for _, port := range ports {
splits := strings.Split(port, "/")
if len(splits) < 1 || len(splits) > 2 {
return nil, fmt.Errorf("unable to parse the port string %s", port)
}

portNumberI64, err := strconv.ParseInt(splits[0], 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid port number %s", splits[0])
}
portNumber := int32(portNumberI64)

var portProto corev1.Protocol
if len(splits) == 2 {
switch strings.ToUpper(splits[1]) {
case "TCP":
portProto = corev1.ProtocolTCP
case "UDP":
portProto = corev1.ProtocolUDP
default:
return nil, fmt.Errorf("invalid port protocol %s", splits[1])
}
} else {
portProto = corev1.ProtocolTCP
}

port := corev1.ContainerPort{
Name: fmt.Sprintf("%d-%s", portNumber, strings.ToLower(string(portProto))),
ContainerPort: portNumber,
Protocol: portProto,
}
containerPorts = append(containerPorts, port)
}
return containerPorts, nil
}

// RemoveEnvVarsFromList removes the env variables based on the keys provided
// and returns a new EnvVarList
func RemoveEnvVarsFromList(envVarList []v1.EnvVar, keys []string) ([]v1.EnvVar, error) {
// convert the envVarList map to an array to easily search for env var(s)
// to remove from the component
envVarListArray := []string{}
for _, env := range envVarList {
envVarListArray = append(envVarListArray, env.Name)
}

// now check if the environment variable(s) requested for removal exists in
// the env vars set for the component by odo
for _, key := range keys {
if !InArray(envVarListArray, key) {
return nil, fmt.Errorf("unable to find environment variable %s in the component", key)
}
}

// finally, let's remove the environment variables(s) requested by the user
newEnvVarList := []v1.EnvVar{}
for _, envVar := range envVarList {
// if the env is in the keys we skip it
if InArray(keys, envVar.Name) {
continue
}
newEnvVarList = append(newEnvVarList, envVar)
}
return newEnvVarList, nil
}

// Merge merges the other EnvVarlist with keeping last value for duplicate EnvVars
// and returns a new EnvVarList
func Merge(original []v1.EnvVar, other []v1.EnvVar) []v1.EnvVar {

var dedupNewEvl []v1.EnvVar
newEvl := append(original, other...)
uniqueMap := make(map[string]string)
// last value will be kept in case of duplicate env vars
for _, envVar := range newEvl {
uniqueMap[envVar.Name] = envVar.Value
}

for key, value := range uniqueMap {
dedupNewEvl = append(dedupNewEvl, v1.EnvVar{
Name: key,
Value: value,
})
}

return dedupNewEvl

}

// In checks if the value is in the array
func InArray(arr []string, value string) bool {
for _, item := range arr {
if item == value {
return true
}
}
return false
}
Loading