Skip to content

Commit

Permalink
Merge branch 'main' into queue-reduction
Browse files Browse the repository at this point in the history
  • Loading branch information
shreddedbacon authored Nov 2, 2022
2 parents 4b3a326 + 158897c commit 8a33483
Show file tree
Hide file tree
Showing 18 changed files with 687 additions and 361 deletions.
42 changes: 21 additions & 21 deletions apis/lagoon/v1beta1/lagoonbuild_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,27 +107,27 @@ type Build struct {

// Project contains the project information from lagoon.
type Project struct {
ID *uint `json:"id,omitempty"`
Name string `json:"name"`
Environment string `json:"environment"`
EnvironmentID *uint `json:"environmentId,omitempty"`
UILink string `json:"uiLink,omitempty"`
GitURL string `json:"gitUrl"`
NamespacePattern string `json:"namespacePattern,omitempty"`
RouterPattern string `json:"routerPattern,omitempty"`
EnvironmentType string `json:"environmentType"`
ProductionEnvironment string `json:"productionEnvironment"`
StandbyEnvironment string `json:"standbyEnvironment"`
DeployTarget string `json:"deployTarget"`
ProjectSecret string `json:"projectSecret"`
SubFolder string `json:"subfolder,omitempty"`
Key []byte `json:"key"`
Monitoring Monitoring `json:"monitoring"`
Variables Variables `json:"variables"`
Registry string `json:"registry,omitempty"`
EnvironmentIdling *int `json:"environmentIdling,omitempty"`
ProjectIdling *int `json:"projectIdling,omitempty"`
StorageCalculatorDisabled *int `json:"storageCalc,omitempty"`
ID *uint `json:"id,omitempty"`
Name string `json:"name"`
Environment string `json:"environment"`
EnvironmentID *uint `json:"environmentId,omitempty"`
UILink string `json:"uiLink,omitempty"`
GitURL string `json:"gitUrl"`
NamespacePattern string `json:"namespacePattern,omitempty"`
RouterPattern string `json:"routerPattern,omitempty"`
EnvironmentType string `json:"environmentType"`
ProductionEnvironment string `json:"productionEnvironment"`
StandbyEnvironment string `json:"standbyEnvironment"`
DeployTarget string `json:"deployTarget"`
ProjectSecret string `json:"projectSecret"`
SubFolder string `json:"subfolder,omitempty"`
Key []byte `json:"key"`
Monitoring Monitoring `json:"monitoring"`
Variables Variables `json:"variables"`
Registry string `json:"registry,omitempty"`
EnvironmentIdling *int `json:"environmentIdling,omitempty"`
ProjectIdling *int `json:"projectIdling,omitempty"`
StorageCalculator *int `json:"storageCalculator,omitempty"`
}

// Variables contains the project and environment variables from lagoon.
Expand Down
4 changes: 2 additions & 2 deletions apis/lagoon/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ spec:
type: string
standbyEnvironment:
type: string
storageCalc:
storageCalculator:
type: integer
subfolder:
type: string
Expand Down
22 changes: 17 additions & 5 deletions controllers/v1beta1/build_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,28 @@ func (r *LagoonBuildReconciler) getOrCreateNamespace(ctx context.Context, namesp
}
// set the auto idling values if they are defined
if lagoonBuild.Spec.Project.EnvironmentIdling != nil {
// eventually deprecate 'lagoon.sh/environmentAutoIdle' for 'lagoon.sh/environmentIdlingEnabled'
nsLabels["lagoon.sh/environmentAutoIdle"] = fmt.Sprintf("%d", *lagoonBuild.Spec.Project.EnvironmentIdling)
if *lagoonBuild.Spec.Project.ProjectIdling == 1 {
nsLabels["lagoon.sh/environmentIdlingEnabled"] = "true"
} else {
nsLabels["lagoon.sh/environmentIdlingEnabled"] = "false"
}
}
if lagoonBuild.Spec.Project.ProjectIdling != nil {
// eventually deprecate 'lagoon.sh/projectAutoIdle' for 'lagoon.sh/projectIdlingEnabled'
nsLabels["lagoon.sh/projectAutoIdle"] = fmt.Sprintf("%d", *lagoonBuild.Spec.Project.ProjectIdling)
if *lagoonBuild.Spec.Project.ProjectIdling == 1 {
nsLabels["lagoon.sh/projectIdlingEnabled"] = "true"
} else {
nsLabels["lagoon.sh/projectIdlingEnabled"] = "false"
}
}
if lagoonBuild.Spec.Project.StorageCalculatorDisabled != nil {
if *lagoonBuild.Spec.Project.StorageCalculatorDisabled != 1 {
nsLabels["lagoon.sh/storageCalculatorDisabled"] = "true"
if lagoonBuild.Spec.Project.StorageCalculator != nil {
if *lagoonBuild.Spec.Project.StorageCalculator == 1 {
nsLabels["lagoon.sh/storageCalculatorEnabled"] = "true"
} else {
nsLabels["lagoon.sh/storageCalculatorDisabled"] = "false"
nsLabels["lagoon.sh/storageCalculatorEnabled"] = "false"
}
}
// add the required lagoon labels to the namespace when creating
Expand Down Expand Up @@ -185,7 +197,7 @@ func (r *LagoonBuildReconciler) getOrCreateNamespace(ctx context.Context, namesp
// if local/regional harbor is enabled
if r.LFFHarborEnabled {
// create the harbor client
lagoonHarbor, err := harbor.NewHarbor(r.Harbor)
lagoonHarbor, err := harbor.New(r.Harbor)
if err != nil {
return fmt.Errorf("Error creating harbor client, check your harbor configuration. Error was: %v", err)
}
Expand Down
8 changes: 6 additions & 2 deletions controllers/v1beta1/podmonitor_buildhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,14 @@ func (r *LagoonMonitorReconciler) updateDeploymentWithLogs(
allContainerLogs, err = r.collectLogs(ctx, req, jobPod)
if err == nil {
if cancel {
cancellationMessage := "Build cancelled"
if cancellationDetails, ok := jobPod.GetAnnotations()["lagoon.sh/cancelReason"]; ok {
cancellationMessage = fmt.Sprintf("%v : %v", cancellationMessage, cancellationDetails)
}
allContainerLogs = append(allContainerLogs, []byte(fmt.Sprintf(`
========================================
Build cancelled
========================================`))...)
%v
========================================`, cancellationMessage))...)
}
} else {
allContainerLogs = []byte(fmt.Sprintf(`
Expand Down
8 changes: 6 additions & 2 deletions controllers/v1beta1/podmonitor_taskhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,14 @@ func (r *LagoonMonitorReconciler) updateTaskWithLogs(
allContainerLogs, err = r.collectLogs(ctx, req, jobPod)
if err == nil {
if cancel {
cancellationMessage := "Task cancelled"
if cancellationDetails, ok := jobPod.GetAnnotations()["lagoon.sh/cancelReason"]; ok {
cancellationMessage = fmt.Sprintf("%v : %v", cancellationMessage, cancellationDetails)
}
allContainerLogs = append(allContainerLogs, []byte(fmt.Sprintf(`
========================================
Task cancelled
========================================`))...)
%v
========================================`, cancellationMessage))...)
}
} else {
allContainerLogs = []byte(fmt.Sprintf(`
Expand Down
55 changes: 55 additions & 0 deletions internal/harbor/harbor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package harbor

import (
"time"

"github.com/go-logr/logr"
harborclientv3 "github.com/mittwald/goharbor-client/v3/apiv2"

harborclientv5 "github.com/mittwald/goharbor-client/v5/apiv2"
"github.com/mittwald/goharbor-client/v5/apiv2/pkg/config"

ctrl "sigs.k8s.io/controller-runtime"
)

// Harbor defines a harbor struct
type Harbor struct {
URL string
Hostname string
API string
Username string
Password string
Log logr.Logger
ClientV3 *harborclientv3.RESTClient
ClientV5 *harborclientv5.RESTClient
DeleteDisabled bool
WebhookAddition bool
RobotPrefix string
ExpiryInterval time.Duration
RotateInterval time.Duration
RobotAccountExpiry time.Duration
ControllerNamespace string
NamespacePrefix string
RandomNamespacePrefix bool
WebhookURL string
WebhookEventTypes []string
LagoonTargetName string
Config *config.Options
}

// New create a new harbor connection.
func New(harbor Harbor) (*Harbor, error) {
harbor.Log = ctrl.Log.WithName("controllers").WithName("HarborIntegration")
c, err := harborclientv3.NewRESTClientForHost(harbor.API, harbor.Username, harbor.Password)
if err != nil {
return nil, err
}
harbor.ClientV3 = c
harbor.Config = &config.Options{}
c2, err := harborclientv5.NewRESTClientForHost(harbor.API, harbor.Username, harbor.Password, harbor.Config)
if err != nil {
return nil, err
}
harbor.ClientV5 = c2
return &harbor, nil
}
137 changes: 137 additions & 0 deletions internal/harbor/harbor_credentialrotation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package harbor

import (
"fmt"
"sort"

"context"
"time"

lagoonv1beta1 "github.com/uselagoon/remote-controller/apis/lagoon/v1beta1"
"github.com/uselagoon/remote-controller/internal/helpers"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// RotateRobotCredentials will attempt to recreate any robot account credentials that need to be rotated.
func (h *Harbor) RotateRobotCredentials(ctx context.Context, cl client.Client) {
opLog := ctrl.Log.WithName("handlers").WithName("RotateRobotCredentials")
namespaces := &corev1.NamespaceList{}
labelRequirements, _ := labels.NewRequirement("lagoon.sh/environmentType", selection.Exists, nil)
// @TODO: do this later so we can only run robot credentials for specific controllers
// labelRequirements2, _ := labels.NewRequirement("lagoon.sh/controller", selection.Equals, []string{h.ControllerNamespace})
listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
client.MatchingLabelsSelector{
Selector: labels.NewSelector().Add(*labelRequirements),
// @TODO: do this later so we can only run robot credentials for specific controllers
// Selector: labels.NewSelector().Add(*labelRequirements).Add(*labelRequirements2),
},
})
if err := cl.List(ctx, namespaces, listOption); err != nil {
opLog.Error(err, fmt.Sprintf("Unable to list namespaces created by Lagoon, there may be none or something went wrong"))
return
}
// go over every namespace that has a lagoon.sh label
// and attempt to create and update the robot account credentials as requred.
for _, ns := range namespaces.Items {
if ns.Status.Phase == corev1.NamespaceTerminating {
// if the namespace is terminating, don't try to renew the robot credentials
opLog.Info(fmt.Sprintf("Namespace %s is being terminated, aborting robot credentials check", ns.ObjectMeta.Name))
continue
}
opLog.Info(fmt.Sprintf("Checking if %s needs robot credentials rotated", ns.ObjectMeta.Name))
// check for running builds!
lagoonBuilds := &lagoonv1beta1.LagoonBuildList{}
listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
client.InNamespace(ns.ObjectMeta.Name),
client.MatchingLabels(map[string]string{
"lagoon.sh/controller": h.ControllerNamespace, // created by this controller
}),
})
if err := cl.List(context.Background(), lagoonBuilds, listOption); err != nil {
opLog.Error(err, fmt.Sprintf("Unable to list Lagoon build pods, there may be none or something went wrong"))
continue
}
runningBuilds := false
sort.Slice(lagoonBuilds.Items, func(i, j int) bool {
return lagoonBuilds.Items[i].ObjectMeta.CreationTimestamp.After(lagoonBuilds.Items[j].ObjectMeta.CreationTimestamp.Time)
})
// if there are any builds pending or running, don't try and refresh the credentials as this
// could break the build
if len(lagoonBuilds.Items) > 0 {
if helpers.ContainsString(
helpers.BuildRunningPendingStatus,
lagoonBuilds.Items[0].Labels["lagoon.sh/buildStatus"],
) {
runningBuilds = true
}
}
if !runningBuilds {
// only continue if there isn't any running builds
robotCreds := &helpers.RegistryCredentials{}
curVer, err := h.GetHarborVersion(ctx)
if err != nil {
// @TODO: resource unknown
opLog.Error(err, "error checking harbor version")
continue
}
if h.UseV2Functions(curVer) {
hProject, err := h.CreateProjectV2(ctx, ns.Labels["lagoon.sh/project"])
if err != nil {
// @TODO: resource unknown
opLog.Error(err, "error getting or creating project")
break
}
time.Sleep(1 * time.Second) // wait 1 seconds
robotCreds, err = h.CreateOrRefreshRobotV2(ctx,
cl,
hProject,
ns.Labels["lagoon.sh/environment"],
ns.ObjectMeta.Name,
h.RobotAccountExpiry)
if err != nil {
opLog.Error(err, "error getting or creating robot account")
continue
}
} else {
hProject, err := h.CreateProject(ctx, ns.Labels["lagoon.sh/project"])
if err != nil {
// @TODO: resource unknown
opLog.Error(err, "error getting or creating project")
continue
}
time.Sleep(1 * time.Second) // wait 1 seconds
robotCreds, err = h.CreateOrRefreshRobot(ctx,
cl,
hProject,
ns.Labels["lagoon.sh/environment"],
ns.ObjectMeta.Name,
time.Now().Add(h.RobotAccountExpiry).Unix())
if err != nil {
opLog.Error(err, "error getting or creating robot account")
continue
}
}
time.Sleep(1 * time.Second) // wait 1 seconds
if robotCreds != nil {
// if we have robotcredentials to create, do that here
if err := UpsertHarborSecret(ctx,
cl,
ns.ObjectMeta.Name,
"lagoon-internal-registry-secret", //secret name in kubernetes
h.Hostname,
robotCreds); err != nil {
opLog.Error(err, "error creating or updating robot account credentials")
continue
}
opLog.Info(fmt.Sprintf("Robot credentials rotated for %s", ns.ObjectMeta.Name))
}
} else {
opLog.Info(fmt.Sprintf("There are running or pending builds in %s, skipping", ns.ObjectMeta.Name))
}
}
}
Loading

0 comments on commit 8a33483

Please sign in to comment.