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

Feature: pod security admission #165

Merged
merged 14 commits into from
Mar 8, 2023
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup Go environment
uses: actions/setup-go@v2.1.3
with:
go-version: 1.16
go-version: 1.19
Copy link
Member

@rm3l rm3l Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we also need to update the Go version in codecov.yml? I can see that this workflow is not triggered on PRs, but only upon a push on main, which makes me think that this may no longer pass on main once this PR is merged.

Copy link
Member

@rm3l rm3l Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, wondering if we should not update the version in go.mod to reflect this? This way, all calls to the setup-go action could simply use the version declared in go.mod (see https://github.com/actions/setup-go#getting-go-version-from-the-gomod-file). But I am not sure about the implications on consumers of the library if the Go version in go.mod is updated. This might be a different issue, that said..

id: go

- name: Check out code into the Go module directory
Expand Down Expand Up @@ -45,7 +45,7 @@ jobs:

- name: Check license
run: |
go get github.com/google/addlicense@latest
go install github.com/google/addlicense@latest
git reset HEAD --hard
make check_license
if [[ $? != 0 ]]
Expand Down
21 changes: 11 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@ require (
github.com/devfile/api/v2 v2.2.0
github.com/devfile/registry-support/registry-library v0.0.0-20221018213054-47b3ffaeadba
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.9
github.com/fsnotify/fsnotify v1.6.0
github.com/go-git/go-git/v5 v5.4.2
github.com/gobwas/glob v0.2.3
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.6
github.com/google/go-cmp v0.5.9
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.4.0
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f
github.com/pkg/errors v0.9.1
github.com/spf13/afero v1.6.0
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.0
github.com/xeipuuv/gojsonschema v1.2.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.21.3
k8s.io/apiextensions-apiserver v0.21.3
k8s.io/apimachinery v0.21.3
k8s.io/client-go v0.21.3
k8s.io/api v0.26.1
k8s.io/apiextensions-apiserver v0.26.1
k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1
k8s.io/klog v1.0.0
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471
sigs.k8s.io/controller-runtime v0.9.5
sigs.k8s.io/yaml v1.2.0
k8s.io/pod-security-admission v0.26.1
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
sigs.k8s.io/controller-runtime v0.14.4
sigs.k8s.io/yaml v1.3.0
)
354 changes: 302 additions & 52 deletions go.sum

Large diffs are not rendered by default.

159 changes: 139 additions & 20 deletions pkg/devfile/generator/generators.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
psaapi "k8s.io/pod-security-admission/api"
)

const (
Expand Down Expand Up @@ -74,6 +75,8 @@ func GetObjectMeta(name, namespace string, labels, annotations map[string]string
}

// GetContainers iterates through all container components, filters out init containers and returns corresponding containers
//
// Deprecated in favor of GetPodTemplateSpec
feloy marked this conversation as resolved.
Show resolved Hide resolved
func GetContainers(devfileObj parser.DevfileObj, options common.DevfileOptions) ([]corev1.Container, error) {
allContainers, err := getAllContainers(devfileObj, options)
if err != nil {
Expand Down Expand Up @@ -120,6 +123,8 @@ func GetContainers(devfileObj parser.DevfileObj, options common.DevfileOptions)
}

// GetInitContainers gets the init container for every preStart devfile event
//
// Deprecated in favor of GetPodTemplateSpec
func GetInitContainers(devfileObj parser.DevfileObj) ([]corev1.Container, error) {
containers, err := getAllContainers(devfileObj, common.DevfileOptions{})
if err != nil {
Expand Down Expand Up @@ -170,23 +175,92 @@ func GetInitContainers(devfileObj parser.DevfileObj) ([]corev1.Container, error)

// DeploymentParams is a struct that contains the required data to create a deployment object
type DeploymentParams struct {
TypeMeta metav1.TypeMeta
ObjectMeta metav1.ObjectMeta
InitContainers []corev1.Container
Containers []corev1.Container
TypeMeta metav1.TypeMeta
ObjectMeta metav1.ObjectMeta
// Deprecated. InitContainers, Containers and Volumes are deprecated and are replaced by PodTemplateSpec.
// A PodTemplateSpec value can be obtained calling GetPodTemplateSpec function, instead of calling GetContainers and GetInitContainers
InitContainers []corev1.Container
// Deprecated, see InitContainers
Containers []corev1.Container
// Deprecated, see InitContainers
Volumes []corev1.Volume
PodTemplateSpec *corev1.PodTemplateSpec
PodSelectorLabels map[string]string
Replicas *int32
}

// GetDeployment gets a deployment object
func GetDeployment(devfileObj parser.DevfileObj, deployParams DeploymentParams) (*appsv1.Deployment, error) {

var deploySpecParams deploymentSpecParams
feloy marked this conversation as resolved.
Show resolved Hide resolved
if deployParams.PodTemplateSpec == nil {
// Deprecated
podTemplateSpecParams := podTemplateSpecParams{
ObjectMeta: deployParams.ObjectMeta,
InitContainers: deployParams.InitContainers,
Containers: deployParams.Containers,
Volumes: deployParams.Volumes,
}
podTemplateSpec, err := getPodTemplateSpec(podTemplateSpecParams)
if err != nil {
return nil, err
}
deploySpecParams = deploymentSpecParams{
PodTemplateSpec: *podTemplateSpec,
PodSelectorLabels: deployParams.PodSelectorLabels,
Replicas: deployParams.Replicas,
}
} else {
deploySpecParams = deploymentSpecParams{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dup with

deploySpecParams = deploymentSpecParams{
PodTemplateSpec: *podTemplateSpec,
PodSelectorLabels: deployParams.PodSelectorLabels,
Replicas: deployParams.Replicas,
, can be move out side of the if-else block?

PodTemplateSpec: *deployParams.PodTemplateSpec,
PodSelectorLabels: deployParams.PodSelectorLabels,
Replicas: deployParams.Replicas,
}
}

containerAnnotations, err := getContainerAnnotations(devfileObj, common.DevfileOptions{})
if err != nil {
return nil, err
}
deployParams.ObjectMeta.Annotations = mergeMaps(deployParams.ObjectMeta.Annotations, containerAnnotations.Deployment)

deployment := &appsv1.Deployment{
TypeMeta: deployParams.TypeMeta,
ObjectMeta: deployParams.ObjectMeta,
Spec: *getDeploymentSpec(deploySpecParams),
}

return deployment, nil
}

// PodTemplateParams is a struct that contains the required data to create a podtemplatespec object
type PodTemplateParams struct {
ObjectMeta metav1.ObjectMeta
// PodSecurityAdmissionPolicy is the policy to be respected by the created pod
// The pod will be patched, if necessary, to respect the policies
PodSecurityAdmissionPolicy psaapi.Policy
}

// GetPodTemplateSpec returns a pod template
// The function:
// - iterates through all container components, filters out init containers and gets corresponding containers
// - gets the init container for every preStart devfile event
// - patches the pod template and containers to satisfy PodSecurityAdmissionPolicy
// - patches the pod template and containers to apply pod and container overrides
func GetPodTemplateSpec(devfileObj parser.DevfileObj, podTemplateParams PodTemplateParams) (*corev1.PodTemplateSpec, error) {
containers, err := GetContainers(devfileObj, common.DevfileOptions{})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the GetContainers and GetInitContainers are being called by this new function introduced. which means the two functions are still in use, and need to be updated upon any changes to GetContainers/GetInitContainers behaviors. why mark the two functions as deprecated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, the GetContainers/GetInitContainers could be removed later from the library interface, by renaming them as getContainers/getInitContainers

if err != nil {
return nil, err
}
initContainers, err := GetInitContainers(devfileObj)
if err != nil {
return nil, err
}

podTemplateSpecParams := podTemplateSpecParams{
ObjectMeta: deployParams.ObjectMeta,
InitContainers: deployParams.InitContainers,
Containers: deployParams.Containers,
Volumes: deployParams.Volumes,
ObjectMeta: podTemplateParams.ObjectMeta,
InitContainers: initContainers,
Containers: containers,
feloy marked this conversation as resolved.
Show resolved Hide resolved
}
var globalAttributes attributes.Attributes
// attributes is not supported in versions less than 2.1.0, so we skip it
Expand All @@ -199,29 +273,74 @@ func GetDeployment(devfileObj parser.DevfileObj, deployParams DeploymentParams)
if err != nil {
return nil, err
}
podTemplateSpec, err := getPodTemplateSpec(globalAttributes, components, podTemplateSpecParams)

podTemplateSpec, err := getPodTemplateSpec(podTemplateSpecParams)
if err != nil {
return nil, err
}
deploySpecParams := deploymentSpecParams{
PodTemplateSpec: *podTemplateSpec,
PodSelectorLabels: deployParams.PodSelectorLabels,
Replicas: deployParams.Replicas,

podTemplateSpec, err = patchForPolicy(podTemplateSpec, podTemplateParams.PodSecurityAdmissionPolicy)
if err != nil {
return nil, err
}

containerAnnotations, err := getContainerAnnotations(devfileObj, common.DevfileOptions{})
if needsPodOverrides(globalAttributes, components) {
patchedPodTemplateSpec, err := applyPodOverrides(globalAttributes, components, podTemplateSpec)
if err != nil {
return nil, err
}
patchedPodTemplateSpec.ObjectMeta = podTemplateSpecParams.ObjectMeta
podTemplateSpec = patchedPodTemplateSpec
}

podTemplateSpec.Spec.Containers, err = applyContainerOverrides(devfileObj, podTemplateSpec.Spec.Containers)
if err != nil {
return nil, err
}
podTemplateSpec.Spec.InitContainers, err = applyContainerOverrides(devfileObj, podTemplateSpec.Spec.InitContainers)
if err != nil {
return nil, err
}
deployParams.ObjectMeta.Annotations = mergeMaps(deployParams.ObjectMeta.Annotations, containerAnnotations.Deployment)

deployment := &appsv1.Deployment{
TypeMeta: deployParams.TypeMeta,
ObjectMeta: deployParams.ObjectMeta,
Spec: *getDeploymentSpec(deploySpecParams),
return podTemplateSpec, nil
}

func applyContainerOverrides(devfileObj parser.DevfileObj, containers []corev1.Container) ([]corev1.Container, error) {
containerComponents, err := devfileObj.Data.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{
ComponentType: v1.ContainerComponentType,
},
})
if err != nil {
return nil, err
}

return deployment, nil
getContainerByName := func(name string) (*corev1.Container, bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do not need to introduce a new function for this purpose.
should use

func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component, error) {
, and set DevfileOptions.FilterByName = <the containerName>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? getContainerByName finds a specific container from the containers passed as parameter, but the method you mentions would find a component from the Devfile.

for _, container := range containers {
if container.Name == name {
return &container, true
}
}
return nil, false
}

result := make([]corev1.Container, 0, len(containers))
for _, comp := range containerComponents {
container, found := getContainerByName(comp.Name)
if !found {
continue
}
if comp.Attributes.Exists(ContainerOverridesAttribute) {
patched, err := containerOverridesHandler(comp, container)
if err != nil {
return nil, err
}
result = append(result, *patched)
} else {
result = append(result, *container)
}
}
return result, nil
}

// PVCParams is a struct to create PVC
Expand Down
Loading