-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy platform provider kubernetes under the plugin directory (#5250)
* Copy kubernetes platformprovider under the plugin Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev> * Remove pipedv0 toolregistry dependency from pipedv1 Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev> --------- Signed-off-by: Shinnosuke Sawada-Dazai <shin@warashi.dev>
- Loading branch information
Showing
55 changed files
with
6,257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
// Copyright 2024 The PipeCD Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package provider | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"sync" | ||
|
||
"go.uber.org/zap" | ||
|
||
"github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin/kubernetes/toolregistry" | ||
"github.com/pipe-cd/pipecd/pkg/config" | ||
) | ||
|
||
type Applier interface { | ||
// ApplyManifest does applying the given manifest. | ||
ApplyManifest(ctx context.Context, manifest Manifest) error | ||
// CreateManifest does creating resource from given manifest. | ||
CreateManifest(ctx context.Context, manifest Manifest) error | ||
// ReplaceManifest does replacing resource from given manifest. | ||
ReplaceManifest(ctx context.Context, manifest Manifest) error | ||
// ForceReplaceManifest does force replacing resource from given manifest. | ||
ForceReplaceManifest(ctx context.Context, manifest Manifest) error | ||
// Delete deletes the given resource from Kubernetes cluster. | ||
Delete(ctx context.Context, key ResourceKey) error | ||
} | ||
|
||
type applier struct { | ||
input config.KubernetesDeploymentInput | ||
platformProvider config.PlatformProviderKubernetesConfig | ||
logger *zap.Logger | ||
toolregistry toolregistry.Registry | ||
|
||
kubectl *Kubectl | ||
initOnce sync.Once | ||
initErr error | ||
} | ||
|
||
func NewApplier(input config.KubernetesDeploymentInput, cp config.PlatformProviderKubernetesConfig, logger *zap.Logger, toolregistry toolregistry.Registry) Applier { | ||
return &applier{ | ||
input: input, | ||
platformProvider: cp, | ||
logger: logger.Named("kubernetes-applier"), | ||
toolregistry: toolregistry, | ||
} | ||
} | ||
|
||
// ApplyManifest does applying the given manifest. | ||
func (a *applier) ApplyManifest(ctx context.Context, manifest Manifest) error { | ||
a.initOnce.Do(func() { | ||
a.kubectl, a.initErr = a.findKubectl(ctx, a.getToolVersionToRun()) | ||
}) | ||
if a.initErr != nil { | ||
return a.initErr | ||
} | ||
|
||
if a.input.AutoCreateNamespace { | ||
err := a.kubectl.CreateNamespace( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
) | ||
if err != nil && !errors.Is(err, errResourceAlreadyExists) { | ||
return err | ||
} | ||
} | ||
|
||
return a.kubectl.Apply( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
manifest, | ||
) | ||
} | ||
|
||
// CreateManifest uses kubectl to create the given manifests. | ||
func (a *applier) CreateManifest(ctx context.Context, manifest Manifest) error { | ||
a.initOnce.Do(func() { | ||
a.kubectl, a.initErr = a.findKubectl(ctx, a.getToolVersionToRun()) | ||
}) | ||
if a.initErr != nil { | ||
return a.initErr | ||
} | ||
|
||
if a.input.AutoCreateNamespace { | ||
err := a.kubectl.CreateNamespace( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
) | ||
if err != nil && !errors.Is(err, errResourceAlreadyExists) { | ||
return err | ||
} | ||
} | ||
|
||
return a.kubectl.Create( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
manifest, | ||
) | ||
} | ||
|
||
// ReplaceManifest uses kubectl to replace the given manifests. | ||
func (a *applier) ReplaceManifest(ctx context.Context, manifest Manifest) error { | ||
a.initOnce.Do(func() { | ||
a.kubectl, a.initErr = a.findKubectl(ctx, a.getToolVersionToRun()) | ||
}) | ||
if a.initErr != nil { | ||
return a.initErr | ||
} | ||
|
||
err := a.kubectl.Replace( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
manifest, | ||
) | ||
if err == nil { | ||
return nil | ||
} | ||
|
||
if errors.Is(err, errorReplaceNotFound) { | ||
return ErrNotFound | ||
} | ||
|
||
return err | ||
} | ||
|
||
// ForceReplaceManifest uses kubectl to forcefully replace the given manifests. | ||
func (a *applier) ForceReplaceManifest(ctx context.Context, manifest Manifest) error { | ||
a.initOnce.Do(func() { | ||
a.kubectl, a.initErr = a.findKubectl(ctx, a.getToolVersionToRun()) | ||
}) | ||
if a.initErr != nil { | ||
return a.initErr | ||
} | ||
|
||
err := a.kubectl.ForceReplace( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(manifest.Key), | ||
manifest, | ||
) | ||
if err == nil { | ||
return nil | ||
} | ||
|
||
if errors.Is(err, errorReplaceNotFound) { | ||
return ErrNotFound | ||
} | ||
|
||
return err | ||
} | ||
|
||
// Delete deletes the given resource from Kubernetes cluster. | ||
// If the resource key is different, this returns ErrNotFound. | ||
func (a *applier) Delete(ctx context.Context, k ResourceKey) (err error) { | ||
a.initOnce.Do(func() { | ||
a.kubectl, a.initErr = a.findKubectl(ctx, a.getToolVersionToRun()) | ||
}) | ||
if a.initErr != nil { | ||
return a.initErr | ||
} | ||
|
||
m, err := a.kubectl.Get( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(k), | ||
k, | ||
) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if k.String() != m.GetAnnotations()[LabelResourceKey] { | ||
return ErrNotFound | ||
} | ||
|
||
return a.kubectl.Delete( | ||
ctx, | ||
a.platformProvider.KubeConfigPath, | ||
a.getNamespaceToRun(k), | ||
k, | ||
) | ||
} | ||
|
||
// getNamespaceToRun returns namespace used on kubectl apply/delete commands. | ||
// priority: config.KubernetesDeploymentInput > kubernetes.ResourceKey | ||
func (a *applier) getNamespaceToRun(k ResourceKey) string { | ||
if a.input.Namespace != "" { | ||
return a.input.Namespace | ||
} | ||
return k.Namespace | ||
} | ||
|
||
// getToolVersionToRun returns version of kubectl which should be used for commands. | ||
// priority: applicationConfig.KubectlVersion > pipedConfig.KubectlVersion | ||
func (a *applier) getToolVersionToRun() string { | ||
if a.input.KubectlVersion != "" { | ||
return a.input.KubectlVersion | ||
} | ||
return a.platformProvider.KubectlVersion | ||
} | ||
|
||
func (a *applier) findKubectl(ctx context.Context, version string) (*Kubectl, error) { | ||
path, err := a.toolregistry.Kubectl(ctx, version) | ||
if err != nil { | ||
return nil, fmt.Errorf("no kubectl %s (%v)", version, err) | ||
} | ||
return NewKubectl(version, path), nil | ||
} | ||
|
||
type multiApplier struct { | ||
appliers []Applier | ||
} | ||
|
||
// NewMultiApplier creates an applier that duplicates its operations to all the provided appliers. | ||
func NewMultiApplier(appliers ...Applier) Applier { | ||
return &multiApplier{ | ||
appliers: appliers, | ||
} | ||
} | ||
|
||
func (a *multiApplier) ApplyManifest(ctx context.Context, manifest Manifest) error { | ||
for _, a := range a.appliers { | ||
if err := a.ApplyManifest(ctx, manifest); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (a *multiApplier) CreateManifest(ctx context.Context, manifest Manifest) error { | ||
for _, a := range a.appliers { | ||
if err := a.CreateManifest(ctx, manifest); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (a *multiApplier) ReplaceManifest(ctx context.Context, manifest Manifest) error { | ||
for _, a := range a.appliers { | ||
if err := a.ReplaceManifest(ctx, manifest); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (a *multiApplier) ForceReplaceManifest(ctx context.Context, manifest Manifest) error { | ||
for _, a := range a.appliers { | ||
if err := a.ForceReplaceManifest(ctx, manifest); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (a *multiApplier) Delete(ctx context.Context, key ResourceKey) error { | ||
for _, a := range a.appliers { | ||
if err := a.Delete(ctx, key); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Copyright 2024 The PipeCD Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package provider | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"go.uber.org/zap" | ||
|
||
"github.com/pipe-cd/pipecd/pkg/cache" | ||
) | ||
|
||
type AppManifestsCache struct { | ||
AppID string | ||
Cache cache.Cache | ||
Logger *zap.Logger | ||
} | ||
|
||
func (c AppManifestsCache) Get(commit string) ([]Manifest, bool) { | ||
key := appManifestsCacheKey(c.AppID, commit) | ||
item, err := c.Cache.Get(key) | ||
if err == nil { | ||
return item.([]Manifest), true | ||
} | ||
|
||
if errors.Is(err, cache.ErrNotFound) { | ||
c.Logger.Info("app manifests were not found in cache", | ||
zap.String("app-id", c.AppID), | ||
zap.String("commit-hash", commit), | ||
) | ||
return nil, false | ||
} | ||
|
||
c.Logger.Error("failed while retrieving app manifests from cache", | ||
zap.String("app-id", c.AppID), | ||
zap.String("commit-hash", commit), | ||
zap.Error(err), | ||
) | ||
return nil, false | ||
} | ||
|
||
func (c AppManifestsCache) Put(commit string, manifests []Manifest) { | ||
key := appManifestsCacheKey(c.AppID, commit) | ||
if err := c.Cache.Put(key, manifests); err != nil { | ||
c.Logger.Error("failed while putting app manifests into cache", | ||
zap.String("app-id", c.AppID), | ||
zap.String("commit-hash", commit), | ||
zap.Error(err), | ||
) | ||
} | ||
} | ||
|
||
func appManifestsCacheKey(appID, commit string) string { | ||
return fmt.Sprintf("%s/%s", appID, commit) | ||
} |
Oops, something went wrong.