use server side apply (#4648)

* update to openshift 4.7 and sbo 0.7.0

* use server side apply for updating deployments

* go mod vendor

* use ApplyDeployment instad Patch and Create

* fix login message

* update unit tests to work with ApplyDeployment function

* fall back to Create/Update for k8s older than 1.16

* abstract fieldManager into const

* fix operator-framework imports
This commit is contained in:
Tomas Kral
2021-05-17 19:05:21 +02:00
committed by GitHub
parent f9ddef6ec3
commit 89ac67e425
1584 changed files with 93934 additions and 46318 deletions

109
go.mod
View File

@@ -3,101 +3,72 @@ module github.com/openshift/odo
go 1.13
require (
github.com/Azure/go-autorest/autorest v0.11.4 // indirect
github.com/Microsoft/go-winio v0.4.15 // indirect
github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2
github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e
github.com/blang/semver v3.5.1+incompatible
github.com/devfile/api/v2 v2.0.0-20210408144711-a313872749ed
github.com/devfile/library v1.0.0-alpha.2.0.20210409194304-7a52b221a48e
github.com/devfile/registry-support/index/generator v0.0.0-20210407161420-cd279527f873
github.com/devfile/registry-support/registry-library v0.0.0-20210407161420-cd279527f873
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible
github.com/docker/go-connections v0.4.1-0.20200120150455-7dc0a2d6ddce
github.com/fatih/color v1.7.0
github.com/fatih/color v1.9.0
github.com/fsnotify/fsnotify v1.4.9
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-git/go-git/v5 v5.2.0
github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe
github.com/golang/mock v1.4.4
github.com/gophercloud/gophercloud v0.2.0 // indirect
github.com/gorilla/mux v1.7.3 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/go-logr/logr v0.3.0 // indirect
github.com/gobwas/glob v0.2.3
github.com/golang/mock v1.5.0
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c
github.com/imdario/mergo v0.3.11 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/kubernetes-sigs/service-catalog v0.2.2
github.com/kylelemons/godebug v1.1.1-0.20190824192725-fa7b53cdfc91
github.com/mattn/go-colorable v0.1.2
github.com/mattn/go-isatty v0.0.13-0.20200128103942-cb30d6282491 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84
github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega v1.10.1
github.com/opencontainers/image-spec v1.0.2-0.20200206005212-79b036d80240 // indirect
github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible
github.com/openshift/client-go v0.0.0-20200116152001-92a2713fa240
github.com/openshift/library-go v0.0.0-20200407165825-2e79bd232e72
github.com/openshift/oc v0.0.0-alpha.0.0.20200305142246-2576e482bf00
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20200422144016-a6acf50218ed
github.com/imdario/mergo v0.3.10 // indirect
github.com/kubernetes-sigs/service-catalog v0.3.1
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
github.com/mattn/go-colorable v0.1.6
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/olekukonko/tablewriter v0.0.1
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.10.2
github.com/openshift/api v0.0.0-20201216151826-78a19e96f9eb
github.com/openshift/client-go v0.0.0-20201214125552-e615e336eb49
github.com/openshift/library-go v0.0.0-20210106214821-c4d0b9c8d55f
github.com/openshift/oc v0.0.0-alpha.0.0.20210325095525-2513fdbb36e2
github.com/operator-framework/api v0.3.20
github.com/operator-framework/operator-lifecycle-manager v0.17.0
github.com/pborman/uuid v1.2.0
github.com/pkg/errors v0.9.1
github.com/posener/complete v1.1.2
github.com/posener/complete v1.1.1
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
github.com/spf13/afero v1.2.2
github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1
github.com/tidwall/gjson v1.6.3
github.com/tidwall/gjson v1.7.3
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/zalando/go-keyring v0.1.0
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221
gopkg.in/AlecAivazis/survey.v1 v1.8.0
github.com/zalando/go-keyring v0.1.1
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6
gopkg.in/AlecAivazis/survey.v1 v1.8.8
gopkg.in/segmentio/analytics-go.v3 v3.1.0
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.19.0
k8s.io/apimachinery v0.19.0
k8s.io/cli-runtime v0.17.0
k8s.io/client-go v12.0.0+incompatible
k8s.io/api v0.20.1
k8s.io/apimachinery v0.20.1
k8s.io/cli-runtime v0.20.1
k8s.io/client-go v0.20.1
k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.0.0
k8s.io/kubectl v0.0.0
k8s.io/klog/v2 v2.4.0
k8s.io/kubectl v0.20.1
sigs.k8s.io/controller-runtime v0.6.4 // indirect
sigs.k8s.io/yaml v1.2.0
)
replace (
bitbucket.org/ww/goautoneg => github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d
github.com/Microsoft/hcsshim => github.com/Microsoft/hcsshim v0.8.7
github.com/apcera/gssapi => github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b
github.com/containers/image => github.com/openshift/containers-image v0.0.0-20190130162819-76de87591e9d
github.com/docker/docker => github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/kr/pty => github.com/creack/pty v1.1.11
github.com/openshift/api => github.com/openshift/api v0.0.0-20200205133042-34f0ec8dab87
k8s.io/api => k8s.io/api v0.17.1
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.17.1
k8s.io/apimachinery => github.com/openshift/kubernetes-apimachinery v0.0.0-20191211181342-5a804e65bdc1
k8s.io/apiserver => k8s.io/apiserver v0.17.1
k8s.io/cli-runtime => github.com/openshift/kubernetes-cli-runtime v0.0.0-20200114162348-c8810ef308ee
k8s.io/client-go => github.com/openshift/kubernetes-client-go v0.0.0-20191211181558-5dcabadb2b45
k8s.io/cloud-provider => k8s.io/cloud-provider v0.17.1
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.17.1
k8s.io/code-generator => k8s.io/code-generator v0.17.1
k8s.io/component-base => k8s.io/component-base v0.17.1
k8s.io/cri-api => k8s.io/cri-api v0.17.1
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.17.1
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.17.1
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.17.1
k8s.io/kube-proxy => k8s.io/kube-proxy v0.17.1
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.17.1
k8s.io/kubectl => github.com/openshift/kubernetes-kubectl v0.0.0-20200211153013-50adac736181
k8s.io/kubelet => k8s.io/kubelet v0.17.1
k8s.io/kubernetes => github.com/openshift/kubernetes v1.17.0-alpha.0.0.20191216151305-079984b0a154
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.17.1
k8s.io/metrics => k8s.io/metrics v0.17.1
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.17.1
vbom.ml/util => github.com/fvbommel/util v0.0.0-20180919145318-efcd4e0f9787
github.com/docker/docker => github.com/docker/docker v1.4.2-0.20191121165722-d1d5f6476656
k8s.io/apimachinery => github.com/openshift/kubernetes-apimachinery v0.0.0-20210108114224-194a87c5b03a
k8s.io/cli-runtime => github.com/openshift/kubernetes-cli-runtime v0.0.0-20210108114725-2ff6add1e911
k8s.io/client-go => github.com/openshift/kubernetes-client-go v0.0.0-20210108114446-0829bdd68114
k8s.io/kubectl => github.com/openshift/kubernetes-kubectl v0.0.0-20210108115031-c0d78c0aeda3
)

902
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -135,7 +135,7 @@ func copyAndFilter(w io.Writer, r io.Reader) ([]byte, error) {
func filteredInformation(s []byte) []byte {
// List of strings to correctly filter
s = bytes.Replace(s, []byte("new-project"), []byte("project create"), -1)
s = bytes.Replace(s, []byte("oc new-project"), []byte("odo project create"), -1)
s = bytes.Replace(s, []byte("<projectname>"), []byte("<project-name>"), -1)
s = bytes.Replace(s, []byte("project <project-name>"), []byte("project set <project-name>"), -1)
s = bytes.Replace(s, []byte("odo projects"), []byte("odo project list"), -1)

View File

@@ -20,7 +20,7 @@ import (
registryLibrary "github.com/devfile/registry-support/registry-library/library"
registryUtil "github.com/openshift/odo/pkg/odo/cli/registry/util"
"github.com/openshift/odo/pkg/util"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"

View File

@@ -1,6 +1,7 @@
package component
import (
"context"
"fmt"
"io"
"os"
@@ -17,7 +18,6 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/fatih/color"
"github.com/pkg/errors"
@@ -412,29 +412,6 @@ func (a Adapter) createOrUpdateComponent(componentExists bool, ei envinfo.EnvSpe
return err
}
// Get ServiceBinding Secret name for each Link
var sbSecrets []string
for _, link := range ei.GetLink() {
sb, err := a.Client.GetKubeClient().GetDynamicResource(kclient.ServiceBindingGroup, kclient.ServiceBindingVersion, kclient.ServiceBindingResource, link.Name)
if err != nil {
return err
}
secret, found, err := unstructured.NestedString(sb.Object, "status", "secret")
if err != nil {
return err
}
if !found {
return fmt.Errorf("unable to find secret in ServiceBinding %s", link.Name)
}
sbSecrets = append(sbSecrets, secret)
}
// set EnvFrom to the container that's supposed to have link to the Operator backed service
containers, err = utils.UpdateContainerWithEnvFromSecrets(containers, a.Devfile, a.devfileRunCmd, ei, sbSecrets)
if err != nil {
return err
}
objectMeta := generator.GetObjectMeta(componentName, a.Client.Namespace, labels, nil)
supervisordInitContainer := kclient.GetBootstrapSupervisordInitContainer()
initContainers, err := utils.GetPreStartInitContainers(a.Devfile, containers)
@@ -502,16 +479,19 @@ func (a Adapter) createOrUpdateComponent(componentExists bool, ei envinfo.EnvSpe
}
klog.V(2).Infof("Creating deployment %v", deployment.Spec.Template.GetName())
klog.V(2).Infof("The component name is %v", componentName)
if componentExists {
// If the component already exists, get the resource version of the deploy before updating
klog.V(2).Info("The component already exists, attempting to update it")
deployment, err = a.Client.GetKubeClient().UpdateDeployment(*deployment)
if a.Client.GetKubeClient().IsSSASupported() {
deployment, err = a.Client.GetKubeClient().ApplyDeployment(*deployment)
} else {
deployment, err = a.Client.GetKubeClient().UpdateDeployment(*deployment)
}
if err != nil {
return err
}
klog.V(2).Infof("Successfully updated component %v", componentName)
oldSvc, err := a.Client.GetKubeClient().KubeClient.CoreV1().Services(a.Client.Namespace).Get(componentName, metav1.GetOptions{})
oldSvc, err := a.Client.GetKubeClient().KubeClient.CoreV1().Services(a.Client.Namespace).Get(context.TODO(), componentName, metav1.GetOptions{})
ownerReference := generator.GetOwnerReference(deployment)
svc.OwnerReferences = append(svc.OwnerReferences, ownerReference)
if err != nil {
@@ -533,14 +513,19 @@ func (a Adapter) createOrUpdateComponent(componentExists bool, ei envinfo.EnvSpe
}
klog.V(2).Infof("Successfully update Service for component %s", componentName)
} else {
err = a.Client.GetKubeClient().KubeClient.CoreV1().Services(a.Client.Namespace).Delete(componentName, &metav1.DeleteOptions{})
err = a.Client.GetKubeClient().KubeClient.CoreV1().Services(a.Client.Namespace).Delete(context.TODO(), componentName, metav1.DeleteOptions{})
if err != nil {
return err
}
}
}
} else {
deployment, err = a.Client.GetKubeClient().CreateDeployment(*deployment)
if a.Client.GetKubeClient().IsSSASupported() {
deployment, err = a.Client.GetKubeClient().ApplyDeployment(*deployment)
} else {
deployment, err = a.Client.GetKubeClient().CreateDeployment(*deployment)
}
if err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package component
import (
"encoding/json"
"testing"
"github.com/devfile/library/pkg/devfile/parser/data"
@@ -108,16 +109,15 @@ func TestCreateOrUpdateComponent(t *testing.T) {
fkclient, fkclientset := occlient.FakeNew()
if tt.running {
fkclientset.Kubernetes.PrependReactor("update", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, &deployment, nil
})
fkclientset.Kubernetes.PrependReactor("patch", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, &deployment, nil
})
if tt.running {
fkclientset.Kubernetes.PrependReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, &deployment, nil
})
}
componentAdapter := New(adapterCtx, *fkclient)
err := componentAdapter.createOrUpdateComponent(tt.running, tt.envInfo)
@@ -298,10 +298,23 @@ func TestDoesComponentExist(t *testing.T) {
fkclient, fkclientset := occlient.FakeNew()
fkWatch := watch.NewFake()
fkclientset.Kubernetes.PrependReactor("patch", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
if patchAction, is := action.(ktesting.PatchAction); is {
patch := patchAction.GetPatch()
var deployment v1.Deployment
err := json.Unmarshal(patch, &deployment)
if err != nil {
t.Errorf("unable to parse deployment %q\n", err)
return false, nil, err
}
return true, &deployment, nil
}
return false, nil, nil
})
fkclientset.Kubernetes.PrependWatchReactor("pods", func(action ktesting.Action) (handled bool, ret watch.Interface, err error) {
return true, fkWatch, nil
})
// DoesComponentExist requires an already started component, so start it.
componentAdapter := New(adapterCtx, *fkclient)
err := componentAdapter.createOrUpdateComponent(false, tt.envInfo)

View File

@@ -1,6 +1,7 @@
package component
import (
"context"
"fmt"
"reflect"
"time"
@@ -99,7 +100,7 @@ func (pw *podWatcher) startWatchThread(adapter *Adapter) {
klog.V(4).Infof("Attempting to acquire watch, attempt #%d", watchAttempts)
var err error
w, err = adapter.Client.GetKubeClient().KubeClient.CoreV1().Pods(adapter.Client.Namespace).Watch(metav1.ListOptions{})
w, err = adapter.Client.GetKubeClient().KubeClient.CoreV1().Pods(adapter.Client.Namespace).Watch(context.TODO(), metav1.ListOptions{})
if err != nil || w == nil {

View File

@@ -1,6 +1,7 @@
package component
import (
"context"
"reflect"
"sort"
"strings"
@@ -40,7 +41,7 @@ type KubernetesPodStatus struct {
func (a Adapter) getDeploymentStatus() (*KubernetesDeploymentStatus, error) {
// 1) Retrieve the deployment
deployment, err := a.Client.GetKubeClient().KubeClient.AppsV1().Deployments(a.Client.Namespace).Get(a.ComponentName, metav1.GetOptions{})
deployment, err := a.Client.GetKubeClient().KubeClient.AppsV1().Deployments(a.Client.Namespace).Get(context.TODO(), a.ComponentName, metav1.GetOptions{})
if err != nil {
klog.V(4).Infof("Unable to retrieve deployment %s in %s ", a.ComponentName, a.Client.Namespace)
return nil, err
@@ -53,7 +54,7 @@ func (a Adapter) getDeploymentStatus() (*KubernetesDeploymentStatus, error) {
deploymentUID := deployment.UID
// 2) Retrieve the replica set that is owned by the deployment; if multiple, go with one with largest generation
replicaSetList, err := a.Client.GetKubeClient().KubeClient.AppsV1().ReplicaSets(a.Client.Namespace).List(metav1.ListOptions{})
replicaSetList, err := a.Client.GetKubeClient().KubeClient.AppsV1().ReplicaSets(a.Client.Namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
@@ -85,7 +86,7 @@ outer:
replicaSetUID := matchingReplicaSets[0].UID
// 3) Retrieves the pods that are owned by the ReplicaSet and return
podList, err := a.Client.GetKubeClient().KubeClient.CoreV1().Pods(a.Client.Namespace).List(metav1.ListOptions{})
podList, err := a.Client.GetKubeClient().KubeClient.CoreV1().Pods(a.Client.Namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}

View File

@@ -191,13 +191,20 @@ func TestGetDeploymentStatus(t *testing.T) {
// Return test case's deployment, when requested
fkclientset.Kubernetes.PrependReactor("get", "*", func(action ktesting.Action) (bool, runtime.Object, error) {
if getAction, is := action.(ktesting.GetAction); is && getAction.GetName() == testComponentName {
return true, &tt.deployment, nil
}
return false, nil, nil
})
// Return test case's deployment, when requested
fkclientset.Kubernetes.PrependReactor("patch", "*", func(action ktesting.Action) (bool, runtime.Object, error) {
if patchAction, is := action.(ktesting.PatchAction); is && patchAction.GetName() == testComponentName {
return true, &tt.deployment, nil
}
return false, nil, nil
})
// Return test cases's replicasets, or pods, when requested
fkclientset.Kubernetes.PrependReactor("list", "*", func(action ktesting.Action) (bool, runtime.Object, error) {
if action.GetResource().Resource == "replicasets" {
@@ -218,10 +225,9 @@ func TestGetDeploymentStatus(t *testing.T) {
// Call the function to test
result, err := componentAdapter.getDeploymentStatus()
// Checks for unexpected error cases
if !tt.wantErr == (err != nil) {
t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr)
t.Fatalf("unexpected error %v, wantErr %v", err, tt.wantErr)
}
if string(result.DeploymentUID) != tt.expectedDeploymentUID {
t.Fatalf("could not find expected deployment UID %s %s", string(result.DeploymentUID), tt.expectedDeploymentUID)

View File

@@ -8,7 +8,6 @@ import (
devfileParser "github.com/devfile/library/pkg/devfile/parser"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
adaptersCommon "github.com/openshift/odo/pkg/devfile/adapters/common"
"github.com/openshift/odo/pkg/envinfo"
"github.com/openshift/odo/pkg/kclient"
"github.com/openshift/odo/pkg/log"
"github.com/openshift/odo/pkg/util"
@@ -225,31 +224,6 @@ func overrideContainerArgs(container *corev1.Container) {
container.Args = append(container.Args, "-c", adaptersCommon.SupervisordConfFile)
}
// UpdateContainerWithEnvFromSecrets adds Secrets to "EnvFrom" of the runtime container
func UpdateContainerWithEnvFromSecrets(containers []corev1.Container, devfile devfileParser.DevfileObj, devfileRunCmd string, ei envinfo.EnvSpecificInfo, secrets []string) ([]corev1.Container, error) {
runCommand, err := adaptersCommon.GetRunCommand(devfile.Data, devfileRunCmd)
if err != nil {
return nil, err
}
for i := range containers {
c := &containers[i]
if c.Name == runCommand.Exec.Component {
for _, secret := range secrets {
c.EnvFrom = append(c.EnvFrom, corev1.EnvFromSource{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: secret,
},
},
})
}
}
}
return containers, nil
}
// GetPreStartInitContainers gets the init container for every preStart devfile event
func GetPreStartInitContainers(devfile devfileParser.DevfileObj, containers []corev1.Container) ([]corev1.Container, error) {

View File

@@ -1,6 +1,7 @@
package kclient
import (
"context"
"encoding/json"
"fmt"
"sort"
@@ -19,6 +20,10 @@ import (
"k8s.io/klog"
)
func boolPtr(b bool) *bool {
return &b
}
// constants for deployments
const (
DeploymentKind = "Deployment"
@@ -38,7 +43,7 @@ const (
// GetDeploymentByName gets a deployment by querying by name
func (c *Client) GetDeploymentByName(name string) (*appsv1.Deployment, error) {
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Get(name, metav1.GetOptions{})
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
return deployment, err
}
@@ -69,11 +74,11 @@ func (c *Client) GetDeploymentFromSelector(selector string) ([]appsv1.Deployment
var err error
if selector != "" {
deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(metav1.ListOptions{
deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
} else {
deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(metav1.ListOptions{
deploymentList, err = c.KubeClient.AppsV1().Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.namespace": c.Namespace}.AsSelector().String(),
})
}
@@ -98,7 +103,7 @@ func getDeploymentCondition(status appsv1.DeploymentStatus, condType appsv1.Depl
// ListDeployments lists all deployments by selector
func (c *Client) ListDeployments(selector string) (*appsv1.DeploymentList, error) {
return c.KubeClient.AppsV1().Deployments(c.Namespace).List(metav1.ListOptions{
return c.KubeClient.AppsV1().Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
}
@@ -109,7 +114,7 @@ func (c *Client) WaitForDeploymentRollout(deploymentName string) (*appsv1.Deploy
s := log.Spinner("Waiting for component to start")
defer s.End(false)
w, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Watch(metav1.ListOptions{FieldSelector: "metadata.name=" + deploymentName})
w, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Watch(context.TODO(), metav1.ListOptions{FieldSelector: "metadata.name=" + deploymentName})
if err != nil {
return nil, errors.Wrapf(err, "unable to watch deployment")
}
@@ -185,18 +190,42 @@ See below for a list of failed events that occured more than %d times during dep
}
}
// CreateDeployment creates a deployment based on the given deployment spec
func resourceAsJson(resource interface{}) string {
data, _ := json.MarshalIndent(resource, " ", " ")
return string(data)
}
// ApplyDeployment updates a deployment based on the given deployment spec
func (c *Client) CreateDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Create(&deploy)
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Create(context.TODO(), &deploy, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to create Deployment %s", deploy.Name)
return nil, errors.Wrapf(err, "unable to update Deployment %s", deploy.Name)
}
return deployment, nil
}
// UpdateDeployment updates a deployment based on the given deployment spec
func (c *Client) UpdateDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Update(&deploy)
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Update(context.TODO(), &deploy, metav1.UpdateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to update Deployment %s", deploy.Name)
}
return deployment, nil
}
// ApplyDeployment creates or updates a deployment based on the given deployment spec
// It is using force:true to make sure that if someone changed one of the values that odo manages,
// odo overrides it with the value it expects instead of failing due to conflict.
func (c *Client) ApplyDeployment(deploy appsv1.Deployment) (*appsv1.Deployment, error) {
data, err := json.Marshal(deploy)
klog.V(5).Infoln("Applying Deployment via server-side apply:")
klog.V(5).Infoln(resourceAsJson(deploy))
if err != nil {
return nil, errors.Wrapf(err, "unable to marshal deployment")
}
deployment, err := c.KubeClient.AppsV1().Deployments(c.Namespace).Patch(context.TODO(), deploy.Name, types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: FieldManager, Force: boolPtr(true)})
if err != nil {
return nil, errors.Wrapf(err, "unable to update Deployment %s", deploy.Name)
}
@@ -215,7 +244,7 @@ func (c *Client) DeleteDeployment(labels map[string]string) error {
// Delete Deployment
klog.V(3).Info("Deleting Deployment")
return c.KubeClient.AppsV1().Deployments(c.Namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
return c.KubeClient.AppsV1().Deployments(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
}
// CreateDynamicResource creates a dynamic custom resource
@@ -230,7 +259,7 @@ func (c *Client) CreateDynamicResource(exampleCustomResource map[string]interfac
klog.V(5).Infoln("Creating resource:")
klog.V(5).Infoln(string(debugOut))
// Create the dynamic resource based on the alm-example for the CRD
_, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Create(deployment, metav1.CreateOptions{})
_, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return err
}
@@ -243,7 +272,7 @@ func (c *Client) CreateDynamicResource(exampleCustomResource map[string]interfac
func (c *Client) ListDynamicResource(group, version, resource string) (*unstructured.UnstructuredList, error) {
deploymentRes := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
list, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).List(metav1.ListOptions{})
list, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
@@ -256,7 +285,7 @@ func (c *Client) ListDynamicResource(group, version, resource string) (*unstruct
func (c *Client) GetDynamicResource(group, version, resource, name string) (*unstructured.Unstructured, error) {
deploymentRes := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
res, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Get(name, metav1.GetOptions{})
res, err := c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
@@ -267,7 +296,7 @@ func (c *Client) GetDynamicResource(group, version, resource, name string) (*uns
func (c *Client) DeleteDynamicResource(name, group, version, resource string) error {
deploymentRes := schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
return c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Delete(name, &metav1.DeleteOptions{})
return c.DynamicClient.Resource(deploymentRes).Namespace(c.Namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
}
// Define a function that is meant to create patch based on the contents of the deployment
@@ -286,7 +315,7 @@ func (c *Client) LinkSecret(secretName, componentName, applicationName string) e
return fmt.Sprintf(`[{ "op": "add", "path": "/spec/template/spec/containers/0/envFrom", "value": [{"secretRef": {"name": "%s"}}] }]`, secretName), nil
}
return c.patchDeployment(componentName, deploymentPatchProvider)
return c.jsonPatchDeployment(componentName, deploymentPatchProvider)
}
// UnlinkSecret unlinks a secret to the Deployment of a component
@@ -308,14 +337,14 @@ func (c *Client) UnlinkSecret(secretName, componentName, applicationName string)
return fmt.Sprintf(`[{"op": "remove", "path": "/spec/template/spec/containers/0/envFrom/%d"}]`, indexForRemoval), nil
}
return c.patchDeployment(componentName, deploymentPatchProvider)
return c.jsonPatchDeployment(componentName, deploymentPatchProvider)
}
// this function will look up the appropriate DC, and execute the specified patch
// the whole point of using patch is to avoid race conditions where we try to update
// deployment while it's being simultaneously updated from another source (for example Kubernetes itself)
// this will result in the triggering of a redeployment
func (c *Client) patchDeployment(deploymentName string, deploymentPatchProvider deploymentPatchProvider) error {
func (c *Client) jsonPatchDeployment(deploymentName string, deploymentPatchProvider deploymentPatchProvider) error {
deployment, err := c.GetDeploymentByName(deploymentName)
if err != nil {
@@ -329,7 +358,7 @@ func (c *Client) patchDeployment(deploymentName string, deploymentPatchProvider
}
// patch the Deployment with the secret
_, err = c.KubeClient.AppsV1().Deployments(c.Namespace).Patch(deploymentName, types.JSONPatchType, []byte(patch))
_, err = c.KubeClient.AppsV1().Deployments(c.Namespace).Patch(context.TODO(), deploymentName, types.JSONPatchType, []byte(patch), metav1.PatchOptions{FieldManager: FieldManager})
if err != nil {
return errors.Wrapf(err, "Deployment not patched %s", deployment.Name)
}
@@ -345,7 +374,7 @@ func (c *Client) patchDeployment(deploymentName string, deploymentPatchProvider
func (c *Client) GetDeploymentLabelValues(label string, selector string) ([]string, error) {
// List DeploymentConfig according to selectors
dcList, err := c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
dcList, err := c.appsClient.Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list DeploymentConfigs")
}
@@ -373,11 +402,11 @@ func (c *Client) GetDeploymentConfigsFromSelector(selector string) ([]appsv1.Dep
var err error
if selector != "" {
dcList, err = c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{
dcList, err = c.appsClient.Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
} else {
dcList, err = c.appsClient.Deployments(c.Namespace).List(metav1.ListOptions{
dcList, err = c.appsClient.Deployments(c.Namespace).List(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.namespace": c.Namespace}.AsSelector().String(),
})
}

View File

@@ -53,7 +53,7 @@ func createFakeDeployment(fkclient *Client, fkclientset *FakeClientset, podName
}
deploy := generator.GetDeployment(deploymentParams)
fkclientset.Kubernetes.PrependReactor("create", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
fkclientset.Kubernetes.PrependReactor("patch", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
if podName == "" {
return true, nil, errors.Errorf("deployment name is empty")
}
@@ -70,7 +70,7 @@ func createFakeDeployment(fkclient *Client, fkclientset *FakeClientset, podName
return true, &deployment, nil
})
createdDeployment, err := fkclient.CreateDeployment(*deploy)
createdDeployment, err := fkclient.ApplyDeployment(*deploy)
if err != nil {
return nil, err
}
@@ -236,7 +236,7 @@ func TestUpdateDeployment(t *testing.T) {
}
deploy := generator.GetDeployment(deploymentParams)
fkclientset.Kubernetes.PrependReactor("update", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
fkclientset.Kubernetes.PrependReactor("patch", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
if tt.deploymentName == "" {
return true, nil, errors.Errorf("deployment name is empty")
}
@@ -252,7 +252,7 @@ func TestUpdateDeployment(t *testing.T) {
return true, &deployment, nil
})
updatedDeployment, err := fkclient.UpdateDeployment(*deploy)
updatedDeployment, err := fkclient.ApplyDeployment(*deploy)
// Checks for unexpected error cases
if !tt.wantErr == (err != nil) {

View File

@@ -1,12 +1,14 @@
package kclient
import (
"context"
"fmt"
"sync"
"github.com/openshift/odo/pkg/log"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
"sync"
)
// We use a mutex here in order to make 100% sure that functions such as CollectEvents
@@ -22,7 +24,7 @@ const (
func (c *Client) CollectEvents(selector string, events map[string]corev1.Event, spinner *log.Status, quit <-chan int) {
// Secondly, we will start a go routine for watching for events related to the pod and update our pod status accordingly.
eventWatcher, err := c.KubeClient.CoreV1().Events(c.Namespace).Watch(metav1.ListOptions{})
eventWatcher, err := c.KubeClient.CoreV1().Events(c.Namespace).Watch(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Warningf("Unable to watch for events: %s", err)
return

View File

@@ -1,6 +1,7 @@
package kclient
import (
"context"
"fmt"
"github.com/pkg/errors"
@@ -14,7 +15,7 @@ func (c *Client) CreateIngress(ingress extensionsv1.Ingress) (*extensionsv1.Ingr
if ingress.GetName() == "" {
return nil, fmt.Errorf("ingress name is empty")
}
ingressObj, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Create(&ingress)
ingressObj, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Create(context.TODO(), &ingress, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrap(err, "error creating ingress")
}
@@ -23,7 +24,7 @@ func (c *Client) CreateIngress(ingress extensionsv1.Ingress) (*extensionsv1.Ingr
// DeleteIngress deletes the given ingress
func (c *Client) DeleteIngress(name string) error {
err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Delete(name, &metav1.DeleteOptions{})
err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete ingress")
}
@@ -32,7 +33,7 @@ func (c *Client) DeleteIngress(name string) error {
// ListIngresses lists all the ingresses based on the given label selector
func (c *Client) ListIngresses(labelSelector string) ([]extensionsv1.Ingress, error) {
ingressList, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).List(metav1.ListOptions{
ingressList, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
@@ -44,6 +45,6 @@ func (c *Client) ListIngresses(labelSelector string) ([]extensionsv1.Ingress, er
// GetIngress gets an ingress based on the given name
func (c *Client) GetIngress(name string) (*extensionsv1.Ingress, error) {
ingress, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Get(name, metav1.GetOptions{})
ingress, err := c.KubeClient.ExtensionsV1beta1().Ingresses(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
return ingress, err
}

View File

@@ -1,9 +1,11 @@
package kclient
import (
"context"
"strings"
"time"
"github.com/blang/semver"
servicecatalogclienset "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
@@ -49,6 +51,9 @@ type Client struct {
DynamicClient dynamic.Interface
discoveryClient discovery.DiscoveryInterface
supportedResources map[string]bool
// Is server side apply supported by cluster
// Use IsSSASupported()
isSSASupported *bool
}
// New creates a new client
@@ -127,7 +132,7 @@ func (c *Client) Delete(labels map[string]string, wait bool) error {
}
// Delete Deployments
klog.V(3).Info("Deleting Deployments")
err := c.appsClient.Deployments(c.Namespace).DeleteCollection(&metav1.DeleteOptions{PropagationPolicy: &deletionPolicy}, metav1.ListOptions{LabelSelector: selector})
err := c.appsClient.Deployments(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{PropagationPolicy: &deletionPolicy}, metav1.ListOptions{LabelSelector: selector})
if err != nil {
errorList = append(errorList, "unable to delete deployments")
}
@@ -156,7 +161,7 @@ func (c *Client) WaitForComponentDeletion(selector string) error {
klog.V(3).Infof("Waiting for component to get deleted")
watcher, err := c.appsClient.Deployments(c.Namespace).Watch(metav1.ListOptions{LabelSelector: selector})
watcher, err := c.appsClient.Deployments(c.Namespace).Watch(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return err
}
@@ -228,3 +233,38 @@ func (c *Client) IsResourceSupported(apiGroup, apiVersion, resourceName string)
}
return supported, nil
}
// IsSSASupported checks if Server Side Apply is supported by cluster
// SSA was introduced in Kubernetes 1.16
// If there is an error while parsing versions, it assumes that SSA is supported by cluster.
// Most of clusters these days are 1.16 and up
func (c *Client) IsSSASupported() bool {
// check if this was done before so we don't query cluster multiple times for the same info
if c.isSSASupported == nil {
versionWithSSA, err := semver.Make("1.16.0")
if err != nil {
klog.Warningf("unable to parse version %q", err)
}
kVersion, err := c.discoveryClient.ServerVersion()
if err != nil {
klog.Warningf("unable to get k8s server version %q", err)
return true
}
klog.V(4).Infof("Kubernetes version is %q", kVersion.String())
cleanupVersion := strings.TrimLeft(kVersion.String(), "v")
serverVersion, err := semver.Make(cleanupVersion)
if err != nil {
klog.Warningf("unable to parse k8s server version %q", err)
return true
}
isSSASupported := versionWithSSA.LE(serverVersion)
c.isSSASupported = &isSSASupported
klog.V(4).Infof("Cluster has support for SSA: %t", *c.isSSASupported)
}
return *c.isSSASupported
}

View File

@@ -0,0 +1,86 @@
package kclient
import (
"fmt"
"runtime"
"testing"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery/fake"
)
func (c *fakeDiscovery) ServerVersion() (*version.Info, error) {
return &c.versionInfo, nil
}
type fakeDiscovery struct {
*fake.FakeDiscovery
versionInfo version.Info
}
func TestClient_IsSSASupported(t *testing.T) {
tests := []struct {
name string
version version.Info
want bool
}{
{
name: "k8s with SSA",
version: version.Info{
Major: "1",
Minor: "16",
GitVersion: "1.16.0+000000",
GitCommit: "",
GitTreeState: "",
BuildDate: "",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
},
want: true,
},
{
name: "k8s without SSA",
version: version.Info{
Major: "1",
Minor: "15",
GitVersion: "1.15.0+000000",
GitCommit: "",
GitTreeState: "",
BuildDate: "",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
},
want: false,
},
{
name: "invalid k8s version",
version: version.Info{
Major: "a",
Minor: "b",
GitVersion: "c",
GitCommit: "",
GitTreeState: "",
BuildDate: "",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}, want: true,
},
}
for _, tt := range tests {
fkclient, _ := FakeNew()
fd := fakeDiscovery{}
fd.versionInfo = tt.version
fkclient.SetDiscoveryInterface(&fd)
t.Run(tt.name, func(t *testing.T) {
if got := fkclient.IsSSASupported(); got != tt.want {
t.Errorf("IsSSASupported() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,6 +1,7 @@
package kclient
import (
"context"
"time"
"github.com/pkg/errors"
@@ -23,7 +24,7 @@ const (
// GetNamespaces return list of existing namespaces that user has access to.
func (c *Client) GetNamespaces() ([]string, error) {
namespaces, err := c.KubeClient.CoreV1().Namespaces().List(metav1.ListOptions{})
namespaces, err := c.KubeClient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list namespaces")
}
@@ -38,7 +39,7 @@ func (c *Client) GetNamespaces() ([]string, error) {
// GetNamespace returns Namespace based on its name
// Errors related to project not being found or forbidden are translated to nil project for compatibility
func (c *Client) GetNamespace(name string) (*corev1.Namespace, error) {
ns, err := c.KubeClient.CoreV1().Namespaces().Get(name, metav1.GetOptions{})
ns, err := c.KubeClient.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
istatus, ok := err.(kerrors.APIStatus)
if ok {
@@ -63,7 +64,7 @@ func (c *Client) CreateNamespace(name string) (*corev1.Namespace, error) {
},
}
newNamespace, err := c.KubeClient.CoreV1().Namespaces().Create(namespace)
newNamespace, err := c.KubeClient.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to create Namespace %s", namespace.ObjectMeta.Name)
}
@@ -76,7 +77,7 @@ func (c *Client) DeleteNamespace(name string, wait bool) error {
var watcher watch.Interface
var err error
if wait {
watcher, err = c.KubeClient.CoreV1().Namespaces().Watch(metav1.ListOptions{
watcher, err = c.KubeClient.CoreV1().Namespaces().Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
})
if err != nil {
@@ -85,7 +86,7 @@ func (c *Client) DeleteNamespace(name string, wait bool) error {
defer watcher.Stop()
}
err = c.KubeClient.CoreV1().Namespaces().Delete(name, &metav1.DeleteOptions{})
err = c.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrapf(err, "unable to delete Namespace %s", name)
}
@@ -162,7 +163,7 @@ func (c *Client) WaitForServiceAccountInNamespace(namespace, serviceAccountName
if namespace == "" || serviceAccountName == "" {
return errors.New("namespace and serviceAccountName cannot be empty")
}
watcher, err := c.KubeClient.CoreV1().ServiceAccounts(namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: serviceAccountName}))
watcher, err := c.KubeClient.CoreV1().ServiceAccounts(namespace).Watch(context.TODO(), metav1.SingleObject(metav1.ObjectMeta{Name: serviceAccountName}))
if err != nil {
return err
}

View File

@@ -1,10 +1,11 @@
package kclient
import (
"context"
"fmt"
"strings"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -33,7 +34,7 @@ func (c *Client) IsCSVSupported() (bool, error) {
// It is equivalent to doing `oc get csvs` using oc cli
func (c *Client) ListClusterServiceVersions() (*olm.ClusterServiceVersionList, error) {
klog.V(3).Infof("Fetching list of operators installed in cluster")
csvs, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).List(v1.ListOptions{})
csvs, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).List(context.TODO(), v1.ListOptions{})
if err != nil {
if kerrors.IsNotFound(err) {
return &olm.ClusterServiceVersionList{}, ErrNoSuchOperator
@@ -45,7 +46,7 @@ func (c *Client) ListClusterServiceVersions() (*olm.ClusterServiceVersionList, e
// GetClusterServiceVersion returns a particular CSV from a list of CSVs
func (c *Client) GetClusterServiceVersion(name string) (olm.ClusterServiceVersion, error) {
csv, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).Get(name, v1.GetOptions{})
csv, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).Get(context.TODO(), name, v1.GetOptions{})
if err != nil {
return olm.ClusterServiceVersion{}, err
}

View File

@@ -2,13 +2,15 @@ package kclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/openshift/odo/pkg/preference"
"io"
"strings"
"time"
"github.com/openshift/odo/pkg/preference"
"github.com/openshift/odo/pkg/log"
"github.com/pkg/errors"
"k8s.io/klog"
@@ -40,7 +42,7 @@ func (c *Client) WaitAndGetPodWithEvents(selector string, desiredPhase corev1.Po
spinner := log.Spinner(waitMessage)
defer spinner.End(false)
w, err := c.KubeClient.CoreV1().Pods(c.Namespace).Watch(metav1.ListOptions{
w, err := c.KubeClient.CoreV1().Pods(c.Namespace).Watch(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
if err != nil {
@@ -193,7 +195,7 @@ func (c *Client) GetPodUsingComponentName(componentName string) (*corev1.Pod, er
// GetOnePodFromSelector gets a pod from the selector
func (c *Client) GetOnePodFromSelector(selector string) (*corev1.Pod, error) {
pods, err := c.KubeClient.CoreV1().Pods(c.Namespace).List(metav1.ListOptions{
pods, err := c.KubeClient.CoreV1().Pods(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
if err != nil {
@@ -240,7 +242,7 @@ func (c *Client) GetPodLogs(podName, containerName string, followLog bool) (io.R
Resource("pods").
SubResource("log").
VersionedParams(&podLogOptions, scheme.ParameterCodec).
Stream()
Stream(context.TODO())
return rd, err
}

View File

@@ -2,18 +2,20 @@ package kclient
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/klog"
"math/big"
"strings"
"time"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/klog"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -38,7 +40,7 @@ func (c *Client) CreateTLSSecret(tlsCertificate []byte, tlsPrivKey []byte, objec
Data: data,
}
secret, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(&secretTemplate)
secret, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(context.TODO(), &secretTemplate, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to create secret %s", objectMeta.Name)
}
@@ -98,7 +100,7 @@ func GenerateSelfSignedCertificate(host string) (SelfSignedCertificate, error) {
// GetSecret returns the Secret object in the given namespace
func (c *Client) GetSecret(name, namespace string) (*corev1.Secret, error) {
secret, err := c.KubeClient.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
secret, err := c.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrapf(err, "unable to get the secret %s", secret)
}
@@ -115,7 +117,7 @@ func (c *Client) CreateSecret(objectMeta metav1.ObjectMeta, data map[string]stri
StringData: data,
}
secret.SetOwnerReferences(append(secret.GetOwnerReferences(), ownerReference))
_, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(&secret)
_, err := c.KubeClient.CoreV1().Secrets(c.Namespace).Create(context.TODO(), &secret, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create secret for %s", objectMeta.Name)
}
@@ -168,7 +170,7 @@ func (c *Client) ListSecrets(labelSelector string) ([]corev1.Secret, error) {
}
}
secretList, err := c.KubeClient.CoreV1().Secrets(c.Namespace).List(listOptions)
secretList, err := c.KubeClient.CoreV1().Secrets(c.Namespace).List(context.TODO(), listOptions)
if err != nil {
return nil, errors.Wrap(err, "unable to get secret list")
}
@@ -180,7 +182,7 @@ func (c *Client) ListSecrets(labelSelector string) ([]corev1.Secret, error) {
func (c *Client) WaitAndGetSecret(name string, namespace string) (*corev1.Secret, error) {
klog.V(3).Infof("Waiting for secret %s to become available", name)
w, err := c.KubeClient.CoreV1().Secrets(namespace).Watch(metav1.ListOptions{
w, err := c.KubeClient.CoreV1().Secrets(namespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
})
if err != nil {

View File

@@ -1,8 +1,11 @@
package kclient
import (
"context"
"encoding/json"
"fmt"
"sort"
"github.com/ghodss/yaml"
scv1beta1 "github.com/kubernetes-sigs/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/openshift/odo/pkg/util"
@@ -11,7 +14,6 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog"
"sort"
)
// serviceInstanceParameters converts a map of variable assignments to a byte encoded json document,
@@ -50,7 +52,7 @@ func (c *Client) CreateServiceInstance(serviceName string, serviceType string, s
},
}
_, err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Create(si)
_, err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Create(context.TODO(), si, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return "", errors.Wrapf(err, "unable to create the service instance %s for the service type %s and plan %s", serviceName, serviceType, servicePlan)
@@ -73,7 +75,7 @@ func (c *Client) CreateServiceInstance(serviceName string, serviceType string, s
// ListServiceInstances returns list service instances
func (c *Client) ListServiceInstances(selector string) ([]scv1beta1.ServiceInstance, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}
@@ -98,12 +100,12 @@ func (c *Client) DeleteServiceInstance(labels map[string]string) error {
// Iterating over serviceInstance List and deleting one by one
for _, serviceInstance := range serviceInstances {
// we need to delete the ServiceBinding before deleting the ServiceInstance
err = c.serviceCatalogClient.ServiceBindings(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
err = c.serviceCatalogClient.ServiceBindings(c.Namespace).Delete(context.TODO(), serviceInstance.Name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceBinding")
}
// now we perform the actual deletion
err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Delete(context.TODO(), serviceInstance.Name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceInstance")
}
@@ -119,7 +121,7 @@ func (c *Client) GetClusterServiceClass(serviceName string) (*scv1beta1.ClusterS
opts := metav1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("spec.externalName", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServiceClasses().List(opts)
searchResults, err := c.serviceCatalogClient.ClusterServiceClasses().List(context.TODO(), opts)
if err != nil {
return nil, fmt.Errorf("unable to search classes by name (%s)", err)
}
@@ -134,7 +136,7 @@ func (c *Client) GetClusterServiceClass(serviceName string) (*scv1beta1.ClusterS
// ListClusterServiceClasses queries the service service catalog to get available clusterServiceClasses
func (c *Client) ListClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, error) {
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(metav1.ListOptions{})
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list cluster service classes")
}
@@ -165,7 +167,7 @@ func (c *Client) ListServiceClassesByCategory() (categories map[string][]scv1bet
// ListClusterServicePlans returns list of available plans
func (c *Client) ListClusterServicePlans() ([]scv1beta1.ClusterServicePlan, error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{})
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to get cluster service plan")
}
@@ -181,7 +183,7 @@ func (c *Client) ListClusterServicePlansByServiceName(serviceName string) ([]scv
FieldSelector: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServicePlans().List(opts)
searchResults, err := c.serviceCatalogClient.ClusterServicePlans().List(context.TODO(), opts)
if err != nil {
return nil, fmt.Errorf("unable to search plans for service name '%s', (%s)", serviceName, err)
}
@@ -190,13 +192,13 @@ func (c *Client) ListClusterServicePlansByServiceName(serviceName string) ([]scv
// GetServiceBinding returns the ServiceBinding named serviceName in the namespace namespace
func (c *Client) GetServiceBinding(serviceName string, namespace string) (*scv1beta1.ServiceBinding, error) {
return c.serviceCatalogClient.ServiceBindings(namespace).Get(serviceName, metav1.GetOptions{})
return c.serviceCatalogClient.ServiceBindings(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
}
// CreateServiceBinding creates a ServiceBinding (essentially a secret) within the namespace of the
// service instance created using the service's parameters.
func (c *Client) CreateServiceBinding(bindingName string, namespace string, labels map[string]string) error {
_, err := c.serviceCatalogClient.ServiceBindings(namespace).Create(
_, err := c.serviceCatalogClient.ServiceBindings(namespace).Create(context.TODO(),
&scv1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: bindingName,
@@ -209,7 +211,7 @@ func (c *Client) CreateServiceBinding(bindingName string, namespace string, labe
},
SecretName: bindingName,
},
})
}, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return errors.Wrap(err, "Creation of the secret failed")
@@ -221,7 +223,7 @@ func (c *Client) CreateServiceBinding(bindingName string, namespace string, labe
// ListMatchingPlans retrieves a map associating service plan name to service plan instance associated with the specified service
// class
func (c *Client) ListMatchingPlans(class scv1beta1.ClusterServiceClass) (plans map[string]scv1beta1.ClusterServicePlan, err error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(context.TODO(), metav1.ListOptions{
FieldSelector: "spec.clusterServiceClassRef.name==" + class.Spec.ExternalID,
})
@@ -236,7 +238,7 @@ func (c *Client) ListMatchingPlans(class scv1beta1.ClusterServiceClass) (plans m
func (c *Client) ListServiceInstanceLabelValues(label string, selector string) ([]string, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}

View File

@@ -1,6 +1,8 @@
package kclient
import (
"context"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -9,7 +11,7 @@ import (
// CreateService generates and creates the service
// commonObjectMeta is the ObjectMeta for the service
func (c *Client) CreateService(svc corev1.Service) (*corev1.Service, error) {
service, err := c.KubeClient.CoreV1().Services(c.Namespace).Create(&svc)
service, err := c.KubeClient.CoreV1().Services(c.Namespace).Create(context.TODO(), &svc, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to create Service for %s", svc.Name)
}
@@ -18,7 +20,7 @@ func (c *Client) CreateService(svc corev1.Service) (*corev1.Service, error) {
// UpdateService updates a service based on the given service spec
func (c *Client) UpdateService(svc corev1.Service) (*corev1.Service, error) {
service, err := c.KubeClient.CoreV1().Services(c.Namespace).Update(&svc)
service, err := c.KubeClient.CoreV1().Services(c.Namespace).Update(context.TODO(), &svc, metav1.UpdateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrapf(err, "unable to update Service for %s", svc.Name)
}
@@ -28,7 +30,7 @@ func (c *Client) UpdateService(svc corev1.Service) (*corev1.Service, error) {
// ListServices returns an array of Service resources which match the
// given selector
func (c *Client) ListServices(selector string) ([]corev1.Service, error) {
serviceList, err := c.KubeClient.CoreV1().Services(c.Namespace).List(metav1.ListOptions{
serviceList, err := c.KubeClient.CoreV1().Services(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
if err != nil {

View File

@@ -9,6 +9,8 @@ import (
corev1 "k8s.io/api/core/v1"
)
const FieldManager = "odo"
// GetInputEnvVarsFromStrings generates corev1.EnvVar values from the array of string key=value pairs
// envVars is the array containing the key=value pairs
func GetInputEnvVarsFromStrings(envVars []string) ([]corev1.EnvVar, error) {

View File

@@ -1,6 +1,8 @@
package kclient
import (
"context"
"github.com/devfile/library/pkg/devfile/generator"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -15,7 +17,7 @@ const (
// CreatePVC creates a PVC resource in the cluster with the given name, size and labels
func (c *Client) CreatePVC(pvc corev1.PersistentVolumeClaim) (*corev1.PersistentVolumeClaim, error) {
createdPvc, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Create(&pvc)
createdPvc, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Create(context.TODO(), &pvc, metav1.CreateOptions{FieldManager: FieldManager})
if err != nil {
return nil, errors.Wrap(err, "unable to create PVC")
}
@@ -24,12 +26,12 @@ func (c *Client) CreatePVC(pvc corev1.PersistentVolumeClaim) (*corev1.Persistent
// DeletePVC deletes the required PVC resource from the cluster
func (c *Client) DeletePVC(pvcName string) error {
return c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Delete(pvcName, &metav1.DeleteOptions{})
return c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Delete(context.TODO(), pvcName, metav1.DeleteOptions{})
}
// ListPVCs returns the PVCs based on the given selector
func (c *Client) ListPVCs(selector string) ([]corev1.PersistentVolumeClaim, error) {
pvcList, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).List(metav1.ListOptions{
pvcList, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
if err != nil {
@@ -56,13 +58,13 @@ func (c *Client) ListPVCNames(selector string) ([]string, error) {
// GetPVCFromName returns the PVC of the given name
func (c *Client) GetPVCFromName(pvcName string) (*corev1.PersistentVolumeClaim, error) {
return c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Get(pvcName, metav1.GetOptions{})
return c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Get(context.TODO(), pvcName, metav1.GetOptions{})
}
// UpdatePVCLabels updates the given PVC with the given labels
func (c *Client) UpdatePVCLabels(pvc *corev1.PersistentVolumeClaim, labels map[string]string) error {
pvc.Labels = labels
_, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(pvc)
_, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(context.TODO(), pvc, metav1.UpdateOptions{FieldManager: FieldManager})
if err != nil {
return errors.Wrap(err, "unable to remove storage label from PVC")
}
@@ -75,14 +77,14 @@ func (c *Client) GetAndUpdateStorageOwnerReference(pvc *corev1.PersistentVolumeC
return errors.New("owner references are empty")
}
// get the latest version of the PVC to avoid conflict errors
latestPVC, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Get(pvc.Name, metav1.GetOptions{})
latestPVC, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{})
if err != nil {
return err
}
for _, owRf := range ownerReference {
latestPVC.SetOwnerReferences(append(pvc.GetOwnerReferences(), owRf))
}
_, err = c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(latestPVC)
_, err = c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(context.TODO(), latestPVC, metav1.UpdateOptions{FieldManager: FieldManager})
if err != nil {
return err
}
@@ -103,7 +105,7 @@ func (c *Client) UpdateStorageOwnerReference(pvc *corev1.PersistentVolumeClaim,
updatedPVC.OwnerReferences = ownerReference
updatedPVC.Spec = pvc.Spec
_, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(updatedPVC)
_, err := c.KubeClient.CoreV1().PersistentVolumeClaims(c.Namespace).Update(context.TODO(), updatedPVC, metav1.UpdateOptions{FieldManager: FieldManager})
if err != nil {
return err
}

View File

@@ -1,12 +1,14 @@
package occlient
import (
"context"
"fmt"
"io"
"time"
buildv1 "github.com/openshift/api/build/v1"
buildschema "github.com/openshift/client-go/build/clientset/versioned/scheme"
"github.com/openshift/odo/pkg/kclient"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -18,7 +20,7 @@ import (
// GetBuildConfigFromName get BuildConfig by its name
func (c *Client) GetBuildConfigFromName(name string) (*buildv1.BuildConfig, error) {
klog.V(3).Infof("Getting BuildConfig: %s", name)
bc, err := c.buildClient.BuildConfigs(c.Namespace).Get(name, metav1.GetOptions{})
bc, err := c.buildClient.BuildConfigs(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrapf(err, "unable to get BuildConfig %s", name)
}
@@ -29,7 +31,7 @@ func (c *Client) GetBuildConfigFromName(name string) (*buildv1.BuildConfig, erro
// buildConfigName is the name of the buildConfig for which we are fetching the build name
// returns the name of the latest build or the error
func (c *Client) GetLatestBuildName(buildConfigName string) (string, error) {
buildConfig, err := c.buildClient.BuildConfigs(c.Namespace).Get(buildConfigName, metav1.GetOptions{})
buildConfig, err := c.buildClient.BuildConfigs(c.Namespace).Get(context.TODO(), buildConfigName, metav1.GetOptions{})
if err != nil {
return "", errors.Wrap(err, "unable to get the latest build name")
}
@@ -59,7 +61,7 @@ func (c *Client) CreateBuildConfig(commonObjectMeta metav1.ObjectMeta, builderIm
if len(envVars) > 0 {
bc.Spec.Strategy.SourceStrategy.Env = envVars
}
_, err = c.buildClient.BuildConfigs(c.Namespace).Create(&bc)
_, err = c.buildClient.BuildConfigs(c.Namespace).Create(context.TODO(), &bc, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return buildv1.BuildConfig{}, errors.Wrapf(err, "unable to create BuildConfig for %s", commonObjectMeta.Name)
}
@@ -91,7 +93,7 @@ func (c *Client) UpdateBuildConfig(buildConfigName string, gitURL string, annota
}
buildConfig.Spec.Source = buildSource
buildConfig.Annotations = annotations
_, err = c.buildClient.BuildConfigs(c.Namespace).Update(buildConfig)
_, err = c.buildClient.BuildConfigs(c.Namespace).Update(context.TODO(), buildConfig, metav1.UpdateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrap(err, "unable to update the component")
}
@@ -107,7 +109,7 @@ func (c *Client) DeleteBuildConfig(commonObjectMeta metav1.ObjectMeta) error {
// Delete BuildConfig
klog.V(3).Info("Deleting BuildConfigs with DeleteBuildConfig")
return c.buildClient.BuildConfigs(c.Namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
return c.buildClient.BuildConfigs(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
}
// WaitForBuildToFinish block and waits for build to finish. Returns error if build failed or was canceled.
@@ -117,7 +119,7 @@ func (c *Client) WaitForBuildToFinish(buildName string, stdout io.Writer, buildT
klog.V(3).Infof("Waiting for %s build to finish", buildName)
// start a watch on the build resources and look for the given build name
w, err := c.buildClient.Builds(c.Namespace).Watch(metav1.ListOptions{
w, err := c.buildClient.Builds(c.Namespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": buildName}.AsSelector().String(),
})
if err != nil {
@@ -170,7 +172,7 @@ func (c *Client) StartBuild(name string) (string, error) {
Name: name,
},
}
result, err := c.buildClient.BuildConfigs(c.Namespace).Instantiate(name, &buildRequest)
result, err := c.buildClient.BuildConfigs(c.Namespace).Instantiate(context.TODO(), name, &buildRequest, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return "", errors.Wrapf(err, "unable to instantiate BuildConfig for %s", name)
}
@@ -193,7 +195,7 @@ func (c *Client) FollowBuildLog(buildName string, stdout io.Writer, buildTimeout
Name(buildName).
SubResource("log").
VersionedParams(&buildLogOptions, buildschema.ParameterCodec).
Stream()
Stream(context.TODO())
if err != nil {
return errors.Wrapf(err, "unable get build log %s", buildName)

View File

@@ -1,6 +1,7 @@
package occlient
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -9,6 +10,7 @@ import (
appsv1 "github.com/openshift/api/apps/v1"
appsschema "github.com/openshift/client-go/apps/clientset/versioned/scheme"
"github.com/openshift/odo/pkg/kclient"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -18,6 +20,10 @@ import (
"k8s.io/klog"
)
func boolPtr(b bool) *bool {
return &b
}
// IsDeploymentConfigSupported checks if DeploymentConfig type is present on the cluster
func (c *Client) IsDeploymentConfigSupported() (bool, error) {
const Group = "apps.openshift.io"
@@ -30,7 +36,7 @@ func (c *Client) IsDeploymentConfigSupported() (bool, error) {
// the Deployment Config name
func (c *Client) GetDeploymentConfigFromName(name string) (*appsv1.DeploymentConfig, error) {
klog.V(3).Infof("Getting DeploymentConfig: %s", name)
deploymentConfig, err := c.appsClient.DeploymentConfigs(c.Namespace).Get(name, metav1.GetOptions{})
deploymentConfig, err := c.appsClient.DeploymentConfigs(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
@@ -67,7 +73,7 @@ func (c *Client) UpdateDCAnnotations(dcName string, annotations map[string]strin
}
dc.Annotations = annotations
_, err = c.appsClient.DeploymentConfigs(c.Namespace).Update(dc)
_, err = c.appsClient.DeploymentConfigs(c.Namespace).Update(context.TODO(), dc, metav1.UpdateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to uDeploymentConfig config %s", dcName)
}
@@ -94,7 +100,7 @@ func (c *Client) patchDC(dcName string, dcPatchProvider dcPatchProvider) error {
}
// patch the DeploymentConfig with the secret
_, err = c.appsClient.DeploymentConfigs(c.Namespace).Patch(dcName, types.JSONPatchType, []byte(patch))
_, err = c.appsClient.DeploymentConfigs(c.Namespace).Patch(context.TODO(), dcName, types.JSONPatchType, []byte(patch), metav1.PatchOptions{FieldManager: kclient.FieldManager, Force: boolPtr(true)})
if err != nil {
return errors.Wrapf(err, "DeploymentConfig not patched %s", dc.Name)
}
@@ -112,11 +118,11 @@ func (c *Client) ListDeploymentConfigs(selector string) ([]appsv1.DeploymentConf
var err error
if selector != "" {
dcList, err = c.appsClient.DeploymentConfigs(c.Namespace).List(metav1.ListOptions{
dcList, err = c.appsClient.DeploymentConfigs(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: selector,
})
} else {
dcList, err = c.appsClient.DeploymentConfigs(c.Namespace).List(metav1.ListOptions{
dcList, err = c.appsClient.DeploymentConfigs(c.Namespace).List(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.namespace": c.Namespace}.AsSelector().String(),
})
}
@@ -135,7 +141,7 @@ func (c *Client) ListDeploymentConfigs(selector string) ([]appsv1.DeploymentConf
// Updated DC and errors if any
func (c *Client) WaitAndGetDC(name string, desiredRevision int64, timeout time.Duration, waitCond func(*appsv1.DeploymentConfig, int64) bool) (*appsv1.DeploymentConfig, error) {
w, err := c.appsClient.DeploymentConfigs(c.Namespace).Watch(metav1.ListOptions{
w, err := c.appsClient.DeploymentConfigs(c.Namespace).Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", name),
})
defer w.Stop()
@@ -180,7 +186,7 @@ func (c *Client) WaitAndGetDC(name string, desiredRevision int64, timeout time.D
func (c *Client) GetDeploymentConfigLabelValues(label string, selector string) ([]string, error) {
// List DeploymentConfig according to selectors
dcList, err := c.appsClient.DeploymentConfigs(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
dcList, err := c.appsClient.DeploymentConfigs(c.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list DeploymentConfigs")
}
@@ -223,7 +229,7 @@ func (c *Client) DisplayDeploymentConfigLog(deploymentConfigName string, followL
Resource("deploymentconfigs").
SubResource("log").
VersionedParams(&deploymentLogOptions, appsschema.ParameterCodec).
Stream()
Stream(context.TODO())
if err != nil {
return errors.Wrapf(err, "unable get deploymentconfigs log %s", deploymentConfigName)
}
@@ -248,7 +254,7 @@ func (c *Client) StartDeployment(deploymentName string) (string, error) {
Latest: true,
Force: true,
}
result, err := c.appsClient.DeploymentConfigs(c.Namespace).Instantiate(deploymentName, &deploymentRequest)
result, err := c.appsClient.DeploymentConfigs(c.Namespace).Instantiate(context.TODO(), deploymentName, &deploymentRequest, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return "", errors.Wrapf(err, "unable to instantiate Deployment for %s", deploymentName)
}
@@ -297,7 +303,7 @@ func (c *Client) AddEnvironmentVariablesToDeploymentConfig(envs []corev1.EnvVar,
dc.Spec.Template.Spec.Containers[0].Env = append(dc.Spec.Template.Spec.Containers[0].Env, envs...)
_, err := c.appsClient.DeploymentConfigs(c.Namespace).Update(dc)
_, err := c.appsClient.DeploymentConfigs(c.Namespace).Update(context.TODO(), dc, metav1.UpdateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to update Deployment Config %v", dc.Name)
}

View File

@@ -1,7 +1,9 @@
package occlient
import (
"fmt"
"os"
"runtime"
"sync"
fakeServiceCatalogClientSet "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/fake"
@@ -14,6 +16,7 @@ import (
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery/fake"
fakeKubeClientset "k8s.io/client-go/kubernetes/fake"
)
@@ -125,3 +128,18 @@ func (c *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
}
return nil, kerrors.NewNotFound(schema.GroupResource{}, "")
}
func (c *fakeDiscovery) ServerVersion() (*version.Info, error) {
versionInfo := version.Info{
Major: "1",
Minor: "16",
GitVersion: "v1.16.0+0000000",
GitCommit: "",
GitTreeState: "",
BuildDate: "",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
return &versionInfo, nil
}

View File

@@ -1,6 +1,7 @@
package occlient
import (
"context"
"encoding/json"
"fmt"
"strconv"
@@ -45,7 +46,7 @@ func (c *Client) GetImageStream(imageNS string, imageName string, imageTag strin
if imageNS == "" {
// First try finding imagestream from current namespace
currentNSImageStream, e := c.imageClient.ImageStreams(currentProjectName).Get(imageName, metav1.GetOptions{})
currentNSImageStream, e := c.imageClient.ImageStreams(currentProjectName).Get(context.TODO(), imageName, metav1.GetOptions{})
if e != nil {
err = errors.Wrapf(e, "no match found for : %s in namespace %s", imageName, currentProjectName)
} else {
@@ -55,7 +56,7 @@ func (c *Client) GetImageStream(imageNS string, imageName string, imageTag strin
}
// If not in current namespace, try finding imagestream from openshift namespace
openshiftNSImageStream, e := c.imageClient.ImageStreams(OpenShiftNameSpace).Get(imageName, metav1.GetOptions{})
openshiftNSImageStream, e := c.imageClient.ImageStreams(OpenShiftNameSpace).Get(context.TODO(), imageName, metav1.GetOptions{})
if e != nil {
// The image is not available in current Namespace.
err = errors.Wrapf(e, "no match found for : %s in namespace %s", imageName, OpenShiftNameSpace)
@@ -74,7 +75,7 @@ func (c *Client) GetImageStream(imageNS string, imageName string, imageTag strin
}
// Fetch imagestream from requested namespace
imageStream, err = c.imageClient.ImageStreams(imageNS).Get(imageName, metav1.GetOptions{})
imageStream, err = c.imageClient.ImageStreams(imageNS).Get(context.TODO(), imageName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrapf(
err, "no match found for %s in namespace %s", imageName, imageNS,
@@ -102,7 +103,7 @@ func (c *Client) GetImageStreamImage(imageStream *imagev1.ImageStream, imageTag
imageStreamImageName := fmt.Sprintf("%s@%s", imageName, tagDigest)
// look for imageStreamImage for given tag (reference by digest)
imageStreamImage, err := c.imageClient.ImageStreamImages(imageNS).Get(imageStreamImageName, metav1.GetOptions{})
imageStreamImage, err := c.imageClient.ImageStreamImages(imageNS).Get(context.TODO(), imageStreamImageName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrapf(err, "unable to find ImageStreamImage with %s digest", imageStreamImageName)
}
@@ -120,7 +121,7 @@ func (c *Client) GetImageStreamImage(imageStream *imagev1.ImageStream, imageTag
// GetImageStreamTags returns all the ImageStreamTag objects in the given namespace
func (c *Client) GetImageStreamTags(namespace string) ([]imagev1.ImageStreamTag, error) {
imageStreamTagList, err := c.imageClient.ImageStreamTags(namespace).List(metav1.ListOptions{})
imageStreamTagList, err := c.imageClient.ImageStreamTags(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list imagestreamtags")
}
@@ -159,7 +160,7 @@ func (c *Client) GetPortsFromBuilderImage(componentType string) ([]string, error
// ListImageStreams returns the Image Stream objects in the given namespace
func (c *Client) ListImageStreams(namespace string) ([]imagev1.ImageStream, error) {
imageStreamList, err := c.imageClient.ImageStreams(namespace).List(metav1.ListOptions{})
imageStreamList, err := c.imageClient.ImageStreams(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list imagestreams")
}

View File

@@ -1,6 +1,7 @@
package occlient
import (
"context"
"encoding/json"
"fmt"
"io"
@@ -274,7 +275,7 @@ func ParseImageName(image string) (string, string, string, string, error) {
// RunLogout logs out the current user from cluster
func (c *Client) RunLogout(stdout io.Writer) error {
output, err := c.userClient.Users().Get("~", metav1.GetOptions{})
output, err := c.userClient.Users().Get(context.TODO(), "~", metav1.GetOptions{})
if err != nil {
klog.V(1).Infof("%v : unable to get userinfo", err)
}
@@ -291,7 +292,7 @@ func (c *Client) RunLogout(stdout io.Writer) error {
}
// deleting token form the server
if err := client.OAuthAccessTokens().Delete(conf.BearerToken, &metav1.DeleteOptions{}); err != nil {
if err := client.OAuthAccessTokens().Delete(context.TODO(), conf.BearerToken, metav1.DeleteOptions{}); err != nil {
klog.V(1).Infof("%v", err)
}
@@ -415,7 +416,7 @@ func (c *Client) NewAppS2I(params CreateArgs, commonObjectMeta metav1.ObjectMeta
is := imagev1.ImageStream{
ObjectMeta: commonObjectMeta,
}
_, err = c.imageClient.ImageStreams(c.Namespace).Create(&is)
_, err = c.imageClient.ImageStreams(c.Namespace).Create(context.TODO(), &is, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create ImageStream for %s", commonObjectMeta.Name)
}
@@ -437,7 +438,7 @@ func (c *Client) NewAppS2I(params CreateArgs, commonObjectMeta metav1.ObjectMeta
if err != nil {
return errors.Wrapf(err, "failed to mount and unmount pvc to dc")
}
createdDC, err := c.appsClient.DeploymentConfigs(c.Namespace).Create(&dc)
createdDC, err := c.appsClient.DeploymentConfigs(c.Namespace).Create(context.TODO(), &dc, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create DeploymentConfig for %s", commonObjectMeta.Name)
}
@@ -574,7 +575,7 @@ func (c *Client) BootstrapSupervisoredS2I(params CreateArgs, commonObjectMeta me
is := imagev1.ImageStream{
ObjectMeta: commonObjectMeta,
}
_, err = c.imageClient.ImageStreams(c.Namespace).Create(&is)
_, err = c.imageClient.ImageStreams(c.Namespace).Create(context.TODO(), &is, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create ImageStream for %s", commonObjectMeta.Name)
}
@@ -628,7 +629,7 @@ func (c *Client) BootstrapSupervisoredS2I(params CreateArgs, commonObjectMeta me
return errors.Wrapf(err, "failed to mount and unmount pvc to dc")
}
createdDC, err := c.appsClient.DeploymentConfigs(c.Namespace).Create(&dc)
createdDC, err := c.appsClient.DeploymentConfigs(c.Namespace).Create(context.TODO(), &dc, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create DeploymentConfig for %s", commonObjectMeta.Name)
}
@@ -734,7 +735,7 @@ func (c *Client) PatchCurrentDC(dc appsv1.DeploymentConfig, prePatchDCHandler dc
// Update the current one that's deployed with the new Spec.
// despite the "patch" function name, we use update since `.Patch` requires
// use to define each and every object we must change. Updating makes it easier.
updatedDc, err := c.appsClient.DeploymentConfigs(c.Namespace).Update(&modifiedDC)
updatedDc, err := c.appsClient.DeploymentConfigs(c.Namespace).Update(context.TODO(), &modifiedDC, metav1.UpdateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to update DeploymentConfig %s", name)
@@ -1045,19 +1046,19 @@ func (c *Client) Delete(labels map[string]string, wait bool) error {
}
// Delete DeploymentConfig
klog.V(3).Info("Deleting DeploymentConfigs")
err := c.appsClient.DeploymentConfigs(c.Namespace).DeleteCollection(&metav1.DeleteOptions{PropagationPolicy: &deletionPolicy}, metav1.ListOptions{LabelSelector: selector})
err := c.appsClient.DeploymentConfigs(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{PropagationPolicy: &deletionPolicy}, metav1.ListOptions{LabelSelector: selector})
if err != nil {
errorList = append(errorList, "unable to delete deploymentconfig")
}
// Delete BuildConfig
klog.V(3).Info("Deleting BuildConfigs")
err = c.buildClient.BuildConfigs(c.Namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
err = c.buildClient.BuildConfigs(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
if err != nil {
errorList = append(errorList, "unable to delete buildconfig")
}
// Delete ImageStream
klog.V(3).Info("Deleting ImageStreams")
err = c.imageClient.ImageStreams(c.Namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
err = c.imageClient.ImageStreams(c.Namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: selector})
if err != nil {
errorList = append(errorList, "unable to delete imagestream")
}
@@ -1086,7 +1087,7 @@ func (c *Client) WaitForComponentDeletion(selector string) error {
klog.V(3).Infof("Waiting for component to get deleted")
watcher, err := c.appsClient.DeploymentConfigs(c.Namespace).Watch(metav1.ListOptions{LabelSelector: selector})
watcher, err := c.appsClient.DeploymentConfigs(c.Namespace).Watch(context.TODO(), metav1.ListOptions{LabelSelector: selector})
if err != nil {
return err
}
@@ -1188,14 +1189,14 @@ func (c *Client) GetServerVersion() (*ServerInfo, error) {
}
// fail fast if user is not connected (same logic as `oc whoami`)
_, err = c.userClient.Users().Get("~", metav1.GetOptions{})
_, err = c.userClient.Users().Get(context.TODO(), "~", metav1.GetOptions{})
if err != nil {
return nil, err
}
// This will fetch the information about OpenShift Version
coreGet := c.kubeClient.KubeClient.CoreV1().RESTClient().Get()
rawOpenShiftVersion, err := coreGet.AbsPath("/version/openshift").Do().Raw()
rawOpenShiftVersion, err := coreGet.AbsPath("/version/openshift").Do(context.TODO()).Raw()
if err != nil {
// when using Minishift (or plain 'oc cluster up' for that matter) with OKD 3.11, the version endpoint is missing...
klog.V(3).Infof("Unable to get OpenShift Version - endpoint '/version/openshift' doesn't exist")
@@ -1208,7 +1209,7 @@ func (c *Client) GetServerVersion() (*ServerInfo, error) {
}
// This will fetch the information about Kubernetes Version
rawKubernetesVersion, err := coreGet.AbsPath("/version").Do().Raw()
rawKubernetesVersion, err := coreGet.AbsPath("/version").Do(context.TODO()).Raw()
if err != nil {
return nil, errors.Wrapf(err, "unable to get Kubernetes Version")
}

View File

@@ -1,10 +1,12 @@
package occlient
import (
"context"
"fmt"
"time"
projectv1 "github.com/openshift/api/project/v1"
"github.com/openshift/odo/pkg/kclient"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -18,7 +20,7 @@ import (
// GetProject returns project based on the name of the project
// errors related to project not being found or forbidden are translated to nil project for compatibility
func (c *Client) GetProject(projectName string) (*projectv1.Project, error) {
prj, err := c.projectClient.Projects().Get(projectName, metav1.GetOptions{})
prj, err := c.projectClient.Projects().Get(context.TODO(), projectName, metav1.GetOptions{})
if err != nil {
istatus, ok := err.(kerrors.APIStatus)
if ok {
@@ -37,7 +39,7 @@ func (c *Client) GetProject(projectName string) (*projectv1.Project, error) {
// ListProjects return list of existing projects that user has access to.
func (c *Client) ListProjects() (*projectv1.ProjectList, error) {
return c.projectClient.Projects().List(metav1.ListOptions{})
return c.projectClient.Projects().List(context.TODO(), metav1.ListOptions{})
}
// ListProjectNames return list of existing project names that user has access to.
@@ -72,7 +74,7 @@ func (c *Client) DeleteProject(name string, wait bool) error {
// If --wait has been passed, we will wait for the project to fully be deleted
if wait {
watcher, err = c.projectClient.Projects().Watch(metav1.ListOptions{
watcher, err = c.projectClient.Projects().Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": name}.AsSelector().String(),
})
if err != nil {
@@ -82,7 +84,7 @@ func (c *Client) DeleteProject(name string, wait bool) error {
}
// Delete the project
err = c.projectClient.Projects().Delete(name, &metav1.DeleteOptions{})
err = c.projectClient.Projects().Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete project")
}
@@ -161,7 +163,7 @@ func (c *Client) CreateNewProject(projectName string, wait bool) error {
var watcher watch.Interface
var err error
if wait {
watcher, err = c.projectClient.Projects().Watch(metav1.ListOptions{
watcher, err = c.projectClient.Projects().Watch(context.TODO(), metav1.ListOptions{
FieldSelector: fields.Set{"metadata.name": projectName}.AsSelector().String(),
})
if err != nil {
@@ -175,7 +177,7 @@ func (c *Client) CreateNewProject(projectName string, wait bool) error {
Name: projectName,
},
}
_, err = c.projectClient.ProjectRequests().Create(projectRequest)
_, err = c.projectClient.ProjectRequests().Create(context.TODO(), projectRequest, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return errors.Wrapf(err, "unable to create new project %s", projectName)
}

View File

@@ -1,8 +1,11 @@
package occlient
import (
"context"
"github.com/devfile/library/pkg/devfile/generator"
routev1 "github.com/openshift/api/route/v1"
"github.com/openshift/odo/pkg/kclient"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
@@ -17,7 +20,7 @@ func (c *Client) IsRouteSupported() (bool, error) {
// GetRoute gets the route with the given name
func (c *Client) GetRoute(name string) (*routev1.Route, error) {
return c.routeClient.Routes(c.Namespace).Get(name, metav1.GetOptions{})
return c.routeClient.Routes(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
}
// CreateRoute creates a route object for the given service and with the given labels
@@ -40,7 +43,7 @@ func (c *Client) CreateRoute(name string, serviceName string, portNumber intstr.
route.SetOwnerReferences(append(route.GetOwnerReferences(), ownerReference))
r, err := c.routeClient.Routes(c.Namespace).Create(route)
r, err := c.routeClient.Routes(c.Namespace).Create(context.TODO(), route, metav1.CreateOptions{FieldManager: kclient.FieldManager})
if err != nil {
return nil, errors.Wrap(err, "error creating route")
}
@@ -49,7 +52,7 @@ func (c *Client) CreateRoute(name string, serviceName string, portNumber intstr.
// DeleteRoute deleted the given route
func (c *Client) DeleteRoute(name string) error {
err := c.routeClient.Routes(c.Namespace).Delete(name, &metav1.DeleteOptions{})
err := c.routeClient.Routes(c.Namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete route")
}
@@ -59,7 +62,7 @@ func (c *Client) DeleteRoute(name string) error {
// ListRoutes lists all the routes based on the given label selector
func (c *Client) ListRoutes(labelSelector string) ([]routev1.Route, error) {
klog.V(3).Infof("Listing routes with label selector: %v", labelSelector)
routeList, err := c.routeClient.Routes(c.Namespace).List(metav1.ListOptions{
routeList, err := c.routeClient.Routes(c.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {

View File

@@ -2,6 +2,7 @@ package occlient
import (
"fmt"
appsv1 "github.com/openshift/api/apps/v1"
imagev1 "github.com/openshift/api/image/v1"
"github.com/openshift/library-go/pkg/apps/appsutil"

View File

@@ -1,10 +1,12 @@
package occlient
import (
"context"
"fmt"
"github.com/devfile/library/pkg/devfile/generator"
appsv1 "github.com/openshift/api/apps/v1"
"github.com/openshift/odo/pkg/kclient"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -142,7 +144,7 @@ func (c *Client) RemoveVolumeFromDeploymentConfig(pvc string, dcName string) err
return err
}
_, updateErr := c.appsClient.DeploymentConfigs(c.Namespace).Update(dc)
_, updateErr := c.appsClient.DeploymentConfigs(c.Namespace).Update(context.TODO(), dc, metav1.UpdateOptions{FieldManager: kclient.FieldManager})
return updateErr
})
if retryErr != nil {

View File

@@ -7,7 +7,7 @@ import (
"github.com/openshift/odo/pkg/machineoutput"
"github.com/openshift/odo/pkg/service"
svc "github.com/openshift/odo/pkg/service"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)

View File

@@ -11,7 +11,7 @@ import (
"github.com/openshift/odo/pkg/machineoutput"
"github.com/openshift/odo/pkg/odo/cli/catalog/util"
"github.com/openshift/odo/pkg/odo/genericclioptions"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/spf13/cobra"
)

View File

@@ -6,7 +6,7 @@ import (
"github.com/openshift/odo/pkg/catalog"
"github.com/openshift/odo/pkg/odo/cli/catalog/util"
"github.com/openshift/odo/pkg/odo/genericclioptions"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/spf13/cobra"
)

View File

@@ -8,7 +8,7 @@ import (
"github.com/openshift/odo/pkg/catalog"
"github.com/openshift/odo/pkg/log"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
)
// DisplayServices displays the specified services

View File

@@ -12,7 +12,6 @@ import (
"github.com/openshift/odo/pkg/odo/genericclioptions"
"github.com/openshift/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
ktemplates "k8s.io/kubectl/pkg/util/templates"
)

View File

@@ -14,7 +14,7 @@ import (
"github.com/ghodss/yaml"
"github.com/openshift/odo/pkg/log"
svc "github.com/openshift/odo/pkg/service"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

View File

@@ -1,6 +1,7 @@
package genericclioptions
import (
"context"
"fmt"
"github.com/openshift/odo/pkg/localConfigProvider"
@@ -63,7 +64,7 @@ func (o *internalCxt) resolveNamespace(configProvider localConfigProvider.LocalC
projectFlag := FlagValueIfSet(command, ProjectFlagName)
if len(projectFlag) > 0 {
// if namespace flag was set, check that the specified namespace exists and use it
_, err := o.KClient.KubeClient.CoreV1().Namespaces().Get(projectFlag, metav1.GetOptions{})
_, err := o.KClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), projectFlag, metav1.GetOptions{})
// do not error out when its odo delete -a, so that we let users delete the local config on missing namespace
if command.HasParent() && command.Parent().Name() != "project" && !(command.Name() == "delete" && command.Flags().Changed("all")) {
util.LogErrorAndExit(err, "")
@@ -80,7 +81,7 @@ func (o *internalCxt) resolveNamespace(configProvider localConfigProvider.LocalC
}
// check that the specified namespace exists
_, err := o.KClient.KubeClient.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{})
_, err := o.KClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
if err != nil {
errFormat := fmt.Sprintf("You don't have permission to create or set namespace '%s' or the namespace doesn't exist. Please create or set a different namespace\n\t", namespace)
// errFormat := fmt.Sprint(e1, "%s project create|set <project_name>")

View File

@@ -1,7 +1,7 @@
package service
import (
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

View File

@@ -17,7 +17,7 @@ import (
scv1beta1 "github.com/kubernetes-sigs/service-catalog/pkg/apis/servicecatalog/v1beta1"
appsv1 "github.com/openshift/api/apps/v1"
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
applabels "github.com/openshift/odo/pkg/application/labels"
componentlabels "github.com/openshift/odo/pkg/component/labels"

View File

@@ -1,6 +1,7 @@
package url
import (
"context"
"fmt"
"net"
"reflect"
@@ -101,14 +102,14 @@ func Create(client *occlient.Client, kClient *kclient.Client, parameters CreateP
ownerReference := generator.GetOwnerReference(deployment)
if parameters.secureURL {
if len(parameters.secretName) != 0 {
_, err := kClient.KubeClient.CoreV1().Secrets(kClient.Namespace).Get(parameters.secretName, metav1.GetOptions{})
_, err := kClient.KubeClient.CoreV1().Secrets(kClient.Namespace).Get(context.TODO(), parameters.secretName, metav1.GetOptions{})
if err != nil {
return "", errors.Wrap(err, "unable to get the provided secret: "+parameters.secretName)
}
}
if len(parameters.secretName) == 0 {
defaultTLSSecretName := parameters.componentName + "-tlssecret"
_, err := kClient.KubeClient.CoreV1().Secrets(kClient.Namespace).Get(defaultTLSSecretName, metav1.GetOptions{})
_, err := kClient.KubeClient.CoreV1().Secrets(kClient.Namespace).Get(context.TODO(), defaultTLSSecretName, metav1.GetOptions{})
// create tls secret if it does not exist
if kerrors.IsNotFound(err) {
selfsignedcert, err := kclient.GenerateSelfSignedCertificate(parameters.host)

View File

@@ -1,12 +0,0 @@
{
"name": "metadata",
"name_pretty": "Google Compute Engine Metadata API",
"product_documentation": "https://cloud.google.com/compute/docs/storing-retrieving-metadata",
"client_documentation": "https://godoc.org/cloud.google.com/go/compute/metadata",
"release_level": "ga",
"language": "go",
"repo": "googleapis/google-cloud-go",
"distribution_name": "cloud.google.com/go/compute/metadata",
"api_id": "compute:metadata",
"requires_billing": false
}

View File

@@ -61,25 +61,14 @@ var (
instID = &cachedValue{k: "instance/id", trim: true}
)
var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)
var defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
// NotDefinedError is returned when requested metadata is not defined.
//
@@ -206,10 +195,9 @@ func systemInfoSuggestsGCE() bool {
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
// Subscribe calls Client.Subscribe on the default client.
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
return defaultClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
@@ -280,9 +268,14 @@ type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
// NewClient returns a Client that can be used to fetch metadata.
// Returns the client that uses the specified http.Client for HTTP requests.
// If nil is specified, returns the default client.
func NewClient(c *http.Client) *Client {
if c == nil {
return defaultClient
}
return &Client{hc: c}
}
@@ -304,7 +297,10 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) {
host = metadataIP
}
u := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", u, nil)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return "", "", err
}
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
@@ -407,11 +403,7 @@ func (c *Client) InstanceTags() ([]string, error) {
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
return c.getTrimmed("instance/name")
}
// Zone returns the current VM's zone, such as "us-central1-b".

View File

@@ -222,6 +222,10 @@ func CheckForUserCompletionWithContext(ctx context.Context, sender Sender, code
case "code_expired":
return nil, ErrDeviceCodeExpired
default:
// return a more meaningful error message if available
if token.ErrorDescription != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, *token.Error, *token.ErrorDescription)
}
return nil, ErrDeviceGeneric
}
}

View File

@@ -5,8 +5,8 @@ go 1.12
require (
github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/Azure/go-autorest/autorest/mocks v0.4.0
github.com/Azure/go-autorest/autorest/mocks v0.4.1
github.com/Azure/go-autorest/tracing v0.6.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
github.com/form3tech-oss/jwt-go v3.2.2+incompatible
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
)

View File

@@ -2,16 +2,16 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c=
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -15,11 +15,24 @@ package adal
// limitations under the License.
import (
"crypto/rsa"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/crypto/pkcs12"
)
var (
// ErrMissingCertificate is returned when no local certificate is found in the provided PFX data.
ErrMissingCertificate = errors.New("adal: certificate missing")
// ErrMissingPrivateKey is returned when no private key is found in the provided PFX data.
ErrMissingPrivateKey = errors.New("adal: private key missing")
)
// LoadToken restores a Token object from a file located at 'path'.
@@ -71,3 +84,52 @@ func SaveToken(path string, mode os.FileMode, token Token) error {
}
return nil
}
// DecodePfxCertificateData extracts the x509 certificate and RSA private key from the provided PFX data.
// The PFX data must contain a private key along with a certificate whose public key matches that of the
// private key or an error is returned.
// If the private key is not password protected pass the empty string for password.
func DecodePfxCertificateData(pfxData []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
blocks, err := pkcs12.ToPEM(pfxData, password)
if err != nil {
return nil, nil, err
}
// first extract the private key
var priv *rsa.PrivateKey
for _, block := range blocks {
if block.Type == "PRIVATE KEY" {
priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, nil, err
}
break
}
}
if priv == nil {
return nil, nil, ErrMissingPrivateKey
}
// now find the certificate with the matching public key of our private key
var cert *x509.Certificate
for _, block := range blocks {
if block.Type == "CERTIFICATE" {
pcert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, err
}
certKey, ok := pcert.PublicKey.(*rsa.PublicKey)
if !ok {
// keep looking
continue
}
if priv.E == certKey.E && priv.N.Cmp(certKey.N) == 0 {
// found a match
cert = pcert
break
}
}
}
if cert == nil {
return nil, nil, ErrMissingCertificate
}
return cert, priv, nil
}

View File

@@ -35,7 +35,7 @@ import (
"time"
"github.com/Azure/go-autorest/autorest/date"
"github.com/dgrijalva/jwt-go"
"github.com/form3tech-oss/jwt-go"
)
const (
@@ -62,6 +62,9 @@ const (
// msiEndpoint is the well known endpoint for getting MSI authentications tokens
msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
// the API version to use for the MSI endpoint
msiAPIVersion = "2018-02-01"
// the default number of attempts to refresh an MSI authentication token
defaultMaxMSIRefreshAttempts = 5
@@ -70,6 +73,9 @@ const (
// asMSISecretEnv is the environment variable used to store the request secret on App Service and Functions
asMSISecretEnv = "MSI_SECRET"
// the API version to use for the App Service MSI endpoint
appServiceAPIVersion = "2017-09-01"
)
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
@@ -354,6 +360,7 @@ type ServicePrincipalToken struct {
customRefreshFunc TokenRefresh
refreshCallbacks []TokenRefreshCallback
// MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token.
// Settings this to a value less than 1 will use the default value.
MaxMSIRefreshAttempts int
}
@@ -650,6 +657,8 @@ func GetMSIVMEndpoint() (string, error) {
return msiEndpoint, nil
}
// NOTE: this only indicates if the ASE environment credentials have been set
// which does not necessarily mean that the caller is authenticating via ASE!
func isAppService() bool {
_, asMSIEndpointEnvExists := os.LookupEnv(asMSIEndpointEnv)
_, asMSISecretEnvExists := os.LookupEnv(asMSISecretEnv)
@@ -678,16 +687,22 @@ func GetMSIEndpoint() (string, error) {
// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the system assigned identity when creating the token.
func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, callbacks...)
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, nil, callbacks...)
}
// NewServicePrincipalTokenFromMSIWithUserAssignedID creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the specified user assigned identity when creating the token.
// It will use the clientID of specified user assigned identity when creating the token.
func NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource string, userAssignedID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, callbacks...)
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, nil, callbacks...)
}
func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
// NewServicePrincipalTokenFromMSIWithIdentityResourceID creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the azure resource id of user assigned identity when creating the token.
func NewServicePrincipalTokenFromMSIWithIdentityResourceID(msiEndpoint, resource string, identityResourceID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, &identityResourceID, callbacks...)
}
func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, identityResourceID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
if err := validateStringParam(msiEndpoint, "msiEndpoint"); err != nil {
return nil, err
}
@@ -699,6 +714,11 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
return nil, err
}
}
if identityResourceID != nil {
if err := validateStringParam(*identityResourceID, "identityResourceID"); err != nil {
return nil, err
}
}
// We set the oauth config token endpoint to be MSI's endpoint
msiEndpointURL, err := url.Parse(msiEndpoint)
if err != nil {
@@ -709,13 +729,16 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
v.Set("resource", resource)
// App Service MSI currently only supports token API version 2017-09-01
if isAppService() {
v.Set("api-version", "2017-09-01")
v.Set("api-version", appServiceAPIVersion)
} else {
v.Set("api-version", "2018-02-01")
v.Set("api-version", msiAPIVersion)
}
if userAssignedID != nil {
v.Set("client_id", *userAssignedID)
}
if identityResourceID != nil {
v.Set("mi_res_id", *identityResourceID)
}
msiEndpointURL.RawQuery = v.Encode()
spt := &ServicePrincipalToken{
@@ -836,11 +859,28 @@ func (spt *ServicePrincipalToken) getGrantType() string {
}
func isIMDS(u url.URL) bool {
imds, err := url.Parse(msiEndpoint)
return isMSIEndpoint(u) == true || isASEEndpoint(u) == true
}
func isMSIEndpoint(endpoint url.URL) bool {
msi, err := url.Parse(msiEndpoint)
if err != nil {
return false
}
return (u.Host == imds.Host && u.Path == imds.Path) || isAppService()
return endpoint.Host == msi.Host && endpoint.Path == msi.Path
}
func isASEEndpoint(endpoint url.URL) bool {
aseEndpoint, err := GetMSIAppServiceEndpoint()
if err != nil {
// app service environment isn't enabled
return false
}
ase, err := url.Parse(aseEndpoint)
if err != nil {
return false
}
return endpoint.Host == ase.Host && endpoint.Path == ase.Path
}
func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error {
@@ -859,7 +899,7 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
}
req.Header.Add("User-Agent", UserAgent())
// Add header when runtime is on App Service or Functions
if isAppService() {
if isASEEndpoint(spt.inner.OauthConfig.TokenEndpoint) {
asMSISecret, _ := os.LookupEnv(asMSISecretEnv)
req.Header.Add("Secret", asMSISecret)
}
@@ -901,6 +941,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
}
var resp *http.Response
if isMSIEndpoint(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = getMSIEndpoint(ctx, spt.sender)
if err != nil {
// return a TokenRefreshError here so that we don't keep retrying
return newTokenRefreshError(fmt.Sprintf("the MSI endpoint is not available. Failed HTTP request to MSI endpoint: %v", err), nil)
}
resp.Body.Close()
}
if isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
} else {
@@ -973,6 +1021,11 @@ func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http
attempt := 0
delay := time.Duration(0)
// maxAttempts is user-specified, ensure that its value is greater than zero else no request will be made
if maxAttempts < 1 {
maxAttempts = defaultMaxMSIRefreshAttempts
}
for attempt < maxAttempts {
if resp != nil && resp.Body != nil {
io.Copy(ioutil.Discard, resp.Body)
@@ -1134,3 +1187,12 @@ func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig,
}
return &m, nil
}
// MSIAvailable returns true if the MSI endpoint is available for authentication.
func MSIAvailable(ctx context.Context, sender Sender) bool {
resp, err := getMSIEndpoint(ctx, sender)
if err == nil {
resp.Body.Close()
}
return err == nil
}

View File

@@ -0,0 +1,36 @@
// +build go1.13
// Copyright 2017 Microsoft Corporation
//
// 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 adal
import (
"context"
"net/http"
"time"
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
// this cannot fail, the return sig is due to legacy reasons
msiEndpoint, _ := GetMSIVMEndpoint()
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
// http.NewRequestWithContext() was added in Go 1.13
req, _ := http.NewRequestWithContext(tempCtx, http.MethodGet, msiEndpoint, nil)
q := req.URL.Query()
q.Add("api-version", msiAPIVersion)
req.URL.RawQuery = q.Encode()
return sender.Do(req)
}

View File

@@ -0,0 +1,36 @@
// +build !go1.13
// Copyright 2017 Microsoft Corporation
//
// 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 adal
import (
"context"
"net/http"
"time"
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
// this cannot fail, the return sig is due to legacy reasons
msiEndpoint, _ := GetMSIVMEndpoint()
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
req, _ := http.NewRequest(http.MethodGet, msiEndpoint, nil)
req = req.WithContext(tempCtx)
q := req.URL.Query()
q.Add("api-version", msiAPIVersion)
req.URL.RawQuery = q.Encode()
return sender.Do(req)
}

View File

@@ -54,12 +54,13 @@ func (sas *SASTokenAuthorizer) WithAuthorization() PrepareDecorator {
return r, err
}
if r.URL.RawQuery == "" {
r.URL.RawQuery = sas.sasToken
} else if !strings.Contains(r.URL.RawQuery, sas.sasToken) {
if r.URL.RawQuery != "" {
r.URL.RawQuery = fmt.Sprintf("%s&%s", r.URL.RawQuery, sas.sasToken)
} else {
r.URL.RawQuery = sas.sasToken
}
r.RequestURI = r.URL.String()
return Prepare(r)
})
}

View File

@@ -152,9 +152,6 @@ func buildCanonicalizedResource(accountName, uri string, keyType SharedKeyType)
// the resource's URI should be encoded exactly as it is in the URI.
// -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx
cr.WriteString(u.EscapedPath())
} else {
// a slash is required to indicate the root path
cr.WriteString("/")
}
params, err := url.ParseQuery(u.RawQuery)

View File

@@ -46,7 +46,6 @@ type ResourceIdentifier struct {
Batch string `json:"batch"`
OperationalInsights string `json:"operationalInsights"`
Storage string `json:"storage"`
Synapse string `json:"synapse"`
}
// Environment represents a set of endpoints for each of Azure's Clouds.
@@ -72,8 +71,6 @@ type Environment struct {
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
TokenAudience string `json:"tokenAudience"`
APIManagementHostNameSuffix string `json:"apiManagementHostNameSuffix"`
SynapseEndpointSuffix string `json:"synapseEndpointSuffix"`
ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"`
}
@@ -101,8 +98,6 @@ var (
ContainerRegistryDNSSuffix: "azurecr.io",
CosmosDBDNSSuffix: "documents.azure.com",
TokenAudience: "https://management.azure.com/",
APIManagementHostNameSuffix: "azure-api.net",
SynapseEndpointSuffix: "dev.azuresynapse.net",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.azure.net",
@@ -110,7 +105,6 @@ var (
Batch: "https://batch.core.windows.net/",
OperationalInsights: "https://api.loganalytics.io",
Storage: "https://storage.azure.com/",
Synapse: "https://dev.azuresynapse.net",
},
}
@@ -137,8 +131,6 @@ var (
ContainerRegistryDNSSuffix: "azurecr.us",
CosmosDBDNSSuffix: "documents.azure.us",
TokenAudience: "https://management.usgovcloudapi.net/",
APIManagementHostNameSuffix: "azure-api.us",
SynapseEndpointSuffix: NotAvailable,
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.usgovcloudapi.net",
@@ -146,7 +138,6 @@ var (
Batch: "https://batch.core.usgovcloudapi.net/",
OperationalInsights: "https://api.loganalytics.us",
Storage: "https://storage.azure.com/",
Synapse: NotAvailable,
},
}
@@ -173,8 +164,6 @@ var (
ContainerRegistryDNSSuffix: "azurecr.cn",
CosmosDBDNSSuffix: "documents.azure.cn",
TokenAudience: "https://management.chinacloudapi.cn/",
APIManagementHostNameSuffix: "azure-api.cn",
SynapseEndpointSuffix: "dev.azuresynapse.azure.cn",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.chinacloudapi.cn/",
KeyVault: "https://vault.azure.cn",
@@ -182,7 +171,6 @@ var (
Batch: "https://batch.chinacloudapi.cn/",
OperationalInsights: NotAvailable,
Storage: "https://storage.azure.com/",
Synapse: "https://dev.azuresynapse.net",
},
}
@@ -209,8 +197,6 @@ var (
ContainerRegistryDNSSuffix: NotAvailable,
CosmosDBDNSSuffix: "documents.microsoftazure.de",
TokenAudience: "https://management.microsoftazure.de/",
APIManagementHostNameSuffix: NotAvailable,
SynapseEndpointSuffix: NotAvailable,
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.cloudapi.de/",
KeyVault: "https://vault.microsoftazure.de",
@@ -218,7 +204,6 @@ var (
Batch: "https://batch.cloudapi.de/",
OperationalInsights: NotAvailable,
Storage: "https://storage.azure.com/",
Synapse: NotAvailable,
},
}
)

View File

@@ -33,7 +33,7 @@ const maxInt = int(^uint(0) >> 1)
// Doc returns un-indented string as here-document.
func Doc(raw string) string {
skipFirstLine := false
if len(raw) > 0 && raw[0] == '\n' {
if raw[0] == '\n' {
raw = raw[1:]
} else {
skipFirstLine = true

View File

@@ -182,14 +182,13 @@ func (s pipeAddress) String() string {
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
return syscall.Handle(0), ctx.Err()
default:
h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err == nil {
return h, nil
}
@@ -198,7 +197,7 @@ func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Hand
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(10 * time.Millisecond)
time.Sleep(time.Millisecond * 10)
}
}
}
@@ -211,7 +210,7 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
if timeout != nil {
absTimeout = time.Now().Add(*timeout)
} else {
absTimeout = time.Now().Add(2 * time.Second)
absTimeout = time.Now().Add(time.Second * 2)
}
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path)
@@ -224,15 +223,9 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
}
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
// cancellation or timeout.
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
var err error
var h syscall.Handle
h, err = tryDialPipe(ctx, &path, access)
h, err = tryDialPipe(ctx, &path)
if err != nil {
return nil, err
}

View File

@@ -5,5 +5,6 @@ os:
- osx
go:
- "1.10.2"
- "1.13.15"
- "1.14.10"
- master

View File

@@ -1,15 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/kr/pty"
packages = ["."]
revision = "282ce0e5322c82529687d609ee670fac7c7d917c"
version = "v1.1.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "ef150ea4826ef379630452277fe5e4539161827fcf6176f9b693f68a4b9c89de"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,34 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/kr/pty"
version = "1.1.1"
[prune]
go-tests = true
unused-packages = true

View File

@@ -1,6 +1,8 @@
# go-expect
[![Build Status](https://travis-ci.org/Netflix/go-expect.svg?branch=master)](https://travis-ci.org/Netflix/go-expect)
![Go](https://github.com/Netflix/go-expect/workflows/Go/badge.svg)
[![codecov](https://codecov.io/gh/Netflix/go-expect/branch/master/graph/badge.svg?token=rZtccXvdCw)](https://codecov.io/gh/Netflix/go-expect)
[![Build Status](https://travis-ci.com/Netflix/go-expect.svg?branch=master)](https://travis-ci.com/Netflix/go-expect)
[![GoDoc](https://godoc.org/github.com/Netflix/go-expect?status.svg)](https://godoc.org/github.com/Netflix/go-expect)
[![NetflixOSS Lifecycle](https://img.shields.io/osslifecycle/Netflix/go-expect.svg)]()

8
vendor/github.com/Netflix/go-expect/go.mod generated vendored Normal file
View File

@@ -0,0 +1,8 @@
module github.com/Netflix/go-expect
go 1.13
require (
github.com/kr/pty v1.1.1
github.com/stretchr/testify v1.6.1
)

12
vendor/github.com/Netflix/go-expect/go.sum generated vendored Normal file
View File

@@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -28,6 +28,18 @@ func (*Digest) Sum64() uint64
This implementation provides a fast pure-Go implementation and an even faster
assembly implementation for amd64.
## Compatibility
This package is in a module and the latest code is in version 2 of the module.
You need a version of Go with at least "minimal module compatibility" to use
github.com/cespare/xxhash/v2:
* 1.9.7+ for Go 1.9
* 1.10.3+ for Go 1.10
* Go 1.11 or later
I recommend using the latest release of Go.
## Benchmarks
Here are some quick benchmarks comparing the pure-Go and assembly

View File

@@ -1,3 +1,3 @@
module github.com/cespare/xxhash/v2
go 1.13
go 1.11

View File

@@ -10,4 +10,4 @@ package xxhash
func Sum64(b []byte) uint64
//go:noescape
func writeBlocks(*Digest, []byte) int
func writeBlocks(d *Digest, b []byte) int

View File

@@ -3,7 +3,7 @@ wincred
Go wrapper around the Windows Credential Manager API functions.
[![Build status](https://ci.appveyor.com/api/projects/status/eclecjwniu2n4u3w/branch/master?svg=true)](https://ci.appveyor.com/project/danieljoos/wincred/branch/master)
![Go](https://github.com/danieljoos/wincred/workflows/Go/badge.svg)
[![GoDoc](https://godoc.org/github.com/danieljoos/wincred?status.svg)](https://godoc.org/github.com/danieljoos/wincred)

View File

@@ -1,24 +0,0 @@
version: '{build}'
clone_folder: c:\gopath\src\github.com\danieljoos\wincred
environment:
GOPATH: c:\gopath
install:
- cmd: >-
rmdir c:\go /s /q
appveyor DownloadFile https://storage.googleapis.com/golang/go1.6.1.windows-amd64.msi
msiexec /i go1.6.1.windows-amd64.msi /q
go version
go env
go get -t
build_script:
- cmd: go install
test_script:
- cmd: >-
cd %GOPATH%/src/github.com/danieljoos/wincred
go test -v -cover

5
vendor/github.com/danieljoos/wincred/go.mod generated vendored Normal file
View File

@@ -0,0 +1,5 @@
module github.com/danieljoos/wincred
go 1.13
require github.com/stretchr/testify v1.5.1

12
vendor/github.com/danieljoos/wincred/go.sum generated vendored Normal file
View File

@@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -23,7 +23,7 @@ type proc interface {
Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
type sysCREDENTIAL struct {
Flags uint32
Type uint32
@@ -39,7 +39,7 @@ type sysCREDENTIAL struct {
UserName *uint16
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374790(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributew
type sysCREDENTIAL_ATTRIBUTE struct {
Keyword *uint16
Flags uint32
@@ -47,7 +47,7 @@ type sysCREDENTIAL_ATTRIBUTE struct {
Value uintptr
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
type sysCRED_TYPE uint32
const (
@@ -58,10 +58,12 @@ const (
sysCRED_TYPE_GENERIC_CERTIFICATE sysCRED_TYPE = 0x5
sysCRED_TYPE_DOMAIN_EXTENDED sysCRED_TYPE = 0x6
sysERROR_NOT_FOUND = "Element not found."
// https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
sysERROR_NOT_FOUND = syscall.Errno(1168)
sysERROR_INVALID_PARAMETER = syscall.Errno(87)
)
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
func sysCredRead(targetName string, typ sysCRED_TYPE) (*Credential, error) {
var pcred *sysCREDENTIAL
targetNamePtr, _ := syscall.UTF16PtrFromString(targetName)
@@ -79,7 +81,7 @@ func sysCredRead(targetName string, typ sysCRED_TYPE) (*Credential, error) {
return sysToCredential(pcred), nil
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa375187(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credwritew
func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error {
ncred := sysFromCredential(cred)
ncred.Type = uint32(typ)
@@ -94,7 +96,7 @@ func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error {
return nil
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374787(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creddeletew
func sysCredDelete(cred *Credential, typ sysCRED_TYPE) error {
targetNamePtr, _ := syscall.UTF16PtrFromString(cred.TargetName)
ret, _, err := procCredDelete.Call(
@@ -109,19 +111,16 @@ func sysCredDelete(cred *Credential, typ sysCRED_TYPE) error {
return nil
}
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374794(v=vs.85).aspx
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credenumeratew
func sysCredEnumerate(filter string, all bool) ([]*Credential, error) {
var count int
var pcreds uintptr
var filterPtr uintptr
var filterPtr *uint16
if !all {
filterUtf16Ptr, _ := syscall.UTF16PtrFromString(filter)
filterPtr = uintptr(unsafe.Pointer(filterUtf16Ptr))
} else {
filterPtr = 0
filterPtr, _ = syscall.UTF16PtrFromString(filter)
}
ret, _, err := procCredEnumerate.Call(
filterPtr,
uintptr(unsafe.Pointer(filterPtr)),
0,
uintptr(unsafe.Pointer(&count)),
uintptr(unsafe.Pointer(&pcreds)),

View File

@@ -2,7 +2,10 @@
package wincred
import "errors"
import (
"errors"
"syscall"
)
const (
sysCRED_TYPE_GENERIC = 0
@@ -12,7 +15,8 @@ const (
sysCRED_TYPE_GENERIC_CERTIFICATE = 0
sysCRED_TYPE_DOMAIN_EXTENDED = 0
sysERROR_NOT_FOUND = ""
sysERROR_NOT_FOUND = syscall.Errno(1)
sysERROR_INVALID_PARAMETER = syscall.Errno(1)
)
func sysCredRead(...interface{}) (*Credential, error) {

View File

@@ -6,7 +6,7 @@ import (
// CredentialPersistence describes one of three persistence modes of a credential.
// A detailed description of the available modes can be found on
// MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
// Docs: https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
type CredentialPersistence uint32
const (
@@ -52,8 +52,8 @@ type Credential struct {
// secrets.
//
// More information about the available kinds of credentials of the Windows
// Credential Management API can be found on MSDN:
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380517(v=vs.85).aspx
// Credential Management API can be found on Docs:
// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
type GenericCredential struct {
Credential
}
@@ -62,8 +62,8 @@ type GenericCredential struct {
// operating system for user logon.
//
// More information about the available kinds of credentials of the Windows
// Credential Management API can be found on MSDN:
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380517(v=vs.85).aspx
// Credential Management API can be found on Docs:
// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
type DomainPassword struct {
Credential
}

View File

@@ -2,9 +2,22 @@
// This includes functions for retrieval, listing and storage of credentials as well as Go structures for convenient access to the credential data.
//
// A more detailed description of Windows Credentials Management can be found on
// MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374789(v=vs.85).aspx
// Docs: https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/credentials-management
package wincred
import "errors"
const (
// ErrElementNotFound is the error that is returned if a requested element cannot be found.
// This error constant can be used to check if a credential could not be found.
ErrElementNotFound = sysERROR_NOT_FOUND
// ErrInvalidParameter is the error that is returned for invalid parameters.
// This error constant can be used to check if the given function parameters were invalid.
// For example when trying to create a new generic credential with an empty target name.
ErrInvalidParameter = sysERROR_INVALID_PARAMETER
)
// GetGenericCredential fetches the generic credential with the given name from Windows credential manager.
// It returns nil and an error if the credential could not be found or an error occurred.
func GetGenericCredential(targetName string) (*GenericCredential, error) {
@@ -77,7 +90,19 @@ func (t *DomainPassword) SetPassword(pw string) {
// List retrieves all credentials of the Credentials store.
func List() ([]*Credential, error) {
creds, err := sysCredEnumerate("", true)
if err != nil && err.Error() == sysERROR_NOT_FOUND {
if err != nil && errors.Is(err, ErrElementNotFound) {
// Ignore ERROR_NOT_FOUND and return an empty list instead
creds = []*Credential{}
err = nil
}
return creds, err
}
// FilteredList retrieves the list of credentials from the Credentials store that match the given filter.
// The filter string defines the prefix followed by an asterisk for the `TargetName` attribute of the credentials.
func FilteredList(filter string) ([]*Credential, error) {
creds, err := sysCredEnumerate(filter, false)
if err != nil && errors.Is(err, ErrElementNotFound) {
// Ignore ERROR_NOT_FOUND and return an empty list instead
creds = []*Credential{}
err = nil

View File

@@ -4,6 +4,7 @@
Aanand Prasad <aanand.prasad@gmail.com>
Aaron Davidson <aaron@databricks.com>
Aaron Feng <aaron.feng@gmail.com>
Aaron Hnatiw <aaron@griddio.com>
Aaron Huslage <huslage@gmail.com>
Aaron L. Xu <liker.xu@foxmail.com>
Aaron Lehmann <aaron.lehmann@docker.com>
@@ -17,6 +18,7 @@ Abhishek Chanda <abhishek.becs@gmail.com>
Abhishek Sharma <abhishek@asharma.me>
Abin Shahab <ashahab@altiscale.com>
Adam Avilla <aavilla@yp.com>
Adam Dobrawy <naczelnik@jawnosc.tk>
Adam Eijdenberg <adam.eijdenberg@gmail.com>
Adam Kunk <adam.kunk@tiaa-cref.org>
Adam Miller <admiller@redhat.com>
@@ -112,6 +114,7 @@ Anda Xu <anda.xu@docker.com>
Anders Janmyr <anders@janmyr.com>
Andre Dublin <81dublin@gmail.com>
Andre Granovsky <robotciti@live.com>
Andrea Denisse Gómez <crypto.andrea@protonmail.ch>
Andrea Luzzardi <aluzzardi@gmail.com>
Andrea Turli <andrea.turli@gmail.com>
Andreas Elvers <andreas@work.de>
@@ -176,8 +179,10 @@ Anusha Ragunathan <anusha.ragunathan@docker.com>
apocas <petermdias@gmail.com>
Arash Deshmeh <adeshmeh@ca.ibm.com>
ArikaChen <eaglesora@gmail.com>
Arko Dasgupta <arko.dasgupta@docker.com>
Arnaud Lefebvre <a.lefebvre@outlook.fr>
Arnaud Porterie <arnaud.porterie@docker.com>
Arnaud Rebillout <arnaud.rebillout@collabora.com>
Arthur Barr <arthur.barr@uk.ibm.com>
Arthur Gautier <baloo@gandi.net>
Artur Meyster <arthurfbi@yahoo.com>
@@ -279,6 +284,7 @@ Carl Loa Odin <carlodin@gmail.com>
Carl X. Su <bcbcarl@gmail.com>
Carlo Mion <mion00@gmail.com>
Carlos Alexandro Becker <caarlos0@gmail.com>
Carlos de Paula <me@carlosedp.com>
Carlos Sanchez <carlos@apache.org>
Carol Fager-Higgins <carol.fager-higgins@docker.com>
Cary <caryhartline@users.noreply.github.com>
@@ -328,6 +334,7 @@ Chris Gibson <chris@chrisg.io>
Chris Khoo <chris.khoo@gmail.com>
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
Chris McKinnel <chrismckinnel@gmail.com>
Chris Price <chris.price@docker.com>
Chris Seto <chriskseto@gmail.com>
Chris Snow <chsnow123@gmail.com>
Chris St. Pierre <chris.a.st.pierre@gmail.com>
@@ -417,12 +424,14 @@ Daniel Norberg <dano@spotify.com>
Daniel Nordberg <dnordberg@gmail.com>
Daniel Robinson <gottagetmac@gmail.com>
Daniel S <dan.streby@gmail.com>
Daniel Sweet <danieljsweet@icloud.com>
Daniel Von Fange <daniel@leancoder.com>
Daniel Watkins <daniel@daniel-watkins.co.uk>
Daniel X Moore <yahivin@gmail.com>
Daniel YC Lin <dlin.tw@gmail.com>
Daniel Zhang <jmzwcn@gmail.com>
Danny Berger <dpb587@gmail.com>
Danny Milosavljevic <dannym@scratchpost.org>
Danny Yates <danny@codeaholics.org>
Danyal Khaliq <danyal.khaliq@tenpearls.com>
Darren Coxall <darren@darrencoxall.com>
@@ -516,6 +525,8 @@ Dmitry Smirnov <onlyjob@member.fsf.org>
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
Dmitry Vorobev <dimahabr@gmail.com>
Dolph Mathews <dolph.mathews@gmail.com>
Dominic Tubach <dominic.tubach@to.com>
Dominic Yin <yindongchao@inspur.com>
Dominik Dingel <dingel@linux.vnet.ibm.com>
Dominik Finkbeiner <finkes93@gmail.com>
Dominik Honnef <dominik@honnef.co>
@@ -584,6 +595,7 @@ Erik Weathers <erikdw@gmail.com>
Erno Hopearuoho <erno.hopearuoho@gmail.com>
Erwin van der Koogh <info@erronis.nl>
Ethan Bell <ebgamer29@gmail.com>
Ethan Mosbaugh <ethan@replicated.com>
Euan Kemp <euan.kemp@coreos.com>
Eugen Krizo <eugen.krizo@gmail.com>
Eugene Yakubovich <eugene.yakubovich@coreos.com>
@@ -620,6 +632,7 @@ Fareed Dudhia <fareeddudhia@googlemail.com>
Fathi Boudra <fathi.boudra@linaro.org>
Federico Gimenez <fgimenez@coit.es>
Felipe Oliveira <felipeweb.programador@gmail.com>
Felipe Ruhland <felipe.ruhland@gmail.com>
Felix Abecassis <fabecassis@nvidia.com>
Felix Geisendörfer <felix@debuggable.com>
Felix Hupfeld <felix@quobyte.com>
@@ -654,6 +667,7 @@ Frank Groeneveld <frank@ivaldi.nl>
Frank Herrmann <fgh@4gh.tv>
Frank Macreery <frank@macreery.com>
Frank Rosquin <frank.rosquin+github@gmail.com>
frankyang <yyb196@gmail.com>
Fred Lifton <fred.lifton@docker.com>
Frederick F. Kautz IV <fkautz@redhat.com>
Frederik Loeffert <frederik@zitrusmedia.de>
@@ -701,6 +715,7 @@ Gleb M Borisov <borisov.gleb@gmail.com>
Glyn Normington <gnormington@gopivotal.com>
GoBella <caili_welcome@163.com>
Goffert van Gool <goffert@phusion.nl>
Goldwyn Rodrigues <rgoldwyn@suse.com>
Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
Gosuke Miyashita <gosukenator@gmail.com>
Gou Rao <gou@portworx.com>
@@ -724,6 +739,7 @@ Guruprasad <lgp171188@gmail.com>
Gustav Sinder <gustav.sinder@gmail.com>
gwx296173 <gaojing3@huawei.com>
Günter Zöchbauer <guenter@gzoechbauer.com>
Haichao Yang <yang.haichao@zte.com.cn>
haikuoliu <haikuo@amazon.com>
Hakan Özler <hakan.ozler@kodcu.com>
Hamish Hutchings <moredhel@aoeu.me>
@@ -732,6 +748,7 @@ Hans Rødtang <hansrodtang@gmail.com>
Hao Shu Wei <haosw@cn.ibm.com>
Hao Zhang <21521210@zju.edu.cn>
Harald Albers <github@albersweb.de>
Harald Niesche <harald@niesche.de>
Harley Laue <losinggeneration@gmail.com>
Harold Cooper <hrldcpr@gmail.com>
Harrison Turton <harrisonturton@gmail.com>
@@ -751,9 +768,11 @@ Hobofan <goisser94@gmail.com>
Hollie Teal <hollie@docker.com>
Hong Xu <hong@topbug.net>
Hongbin Lu <hongbin034@gmail.com>
Hongxu Jia <hongxu.jia@windriver.com>
hsinko <21551195@zju.edu.cn>
Hu Keping <hukeping@huawei.com>
Hu Tao <hutao@cn.fujitsu.com>
HuanHuan Ye <logindaveye@gmail.com>
Huanzhong Zhang <zhanghuanzhong90@gmail.com>
Huayi Zhang <irachex@gmail.com>
Hugo Duncan <hugo@hugoduncan.org>
@@ -897,6 +916,7 @@ Jie Luo <luo612@zju.edu.cn>
Jihyun Hwang <jhhwang@telcoware.com>
Jilles Oldenbeuving <ojilles@gmail.com>
Jim Alateras <jima@comware.com.au>
Jim Ehrismann <jim.ehrismann@docker.com>
Jim Galasyn <jim.galasyn@docker.com>
Jim Minter <jminter@redhat.com>
Jim Perrin <jperrin@centos.org>
@@ -934,7 +954,7 @@ John Feminella <jxf@jxf.me>
John Gardiner Myers <jgmyers@proofpoint.com>
John Gossman <johngos@microsoft.com>
John Harris <john@johnharris.io>
John Howard (VM) <John.Howard@microsoft.com>
John Howard <github@lowenna.com>
John Laswell <john.n.laswell@gmail.com>
John Maguire <jmaguire@duosecurity.com>
John Mulhausen <john@docker.com>
@@ -948,6 +968,7 @@ John Willis <john.willis@docker.com>
Jon Johnson <jonjohnson@google.com>
Jon Surrell <jon.surrell@gmail.com>
Jon Wedaman <jweede@gmail.com>
Jonas Dohse <jonas@dohse.ch>
Jonas Pfenniger <jonas@pfenniger.name>
Jonathan A. Schweder <jonathanschweder@gmail.com>
Jonathan A. Sternberg <jonathansternberg@gmail.com>
@@ -1001,6 +1022,7 @@ Julio Montes <imc.coder@gmail.com>
Jun-Ru Chang <jrjang@gmail.com>
Jussi Nummelin <jussi.nummelin@gmail.com>
Justas Brazauskas <brazauskasjustas@gmail.com>
Justen Martin <jmart@the-coder.com>
Justin Cormack <justin.cormack@docker.com>
Justin Force <justin.force@gmail.com>
Justin Menga <justin.menga@gmail.com>
@@ -1009,6 +1031,7 @@ Justin Simonelis <justin.p.simonelis@gmail.com>
Justin Terry <juterry@microsoft.com>
Justyn Temme <justyntemme@gmail.com>
Jyrki Puttonen <jyrkiput@gmail.com>
Jérémy Leherpeur <amenophis@leherpeur.net>
Jérôme Petazzoni <jerome.petazzoni@docker.com>
Jörg Thalheim <joerg@higgsboson.tk>
K. Heller <pestophagous@gmail.com>
@@ -1046,6 +1069,7 @@ Ken Reese <krrgithub@gmail.com>
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
Kenjiro Nakayama <nakayamakenjiro@gmail.com>
Kent Johnson <kentoj@gmail.com>
Kenta Tada <Kenta.Tada@sony.com>
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
Kevin Burke <kev@inburke.com>
Kevin Clark <kevin.clark@gmail.com>
@@ -1056,6 +1080,7 @@ Kevin Kern <kaiwentan@harmonycloud.cn>
Kevin Menard <kevin@nirvdrum.com>
Kevin Meredith <kevin.m.meredith@gmail.com>
Kevin P. Kucharczyk <kevinkucharczyk@gmail.com>
Kevin Parsons <kevpar@microsoft.com>
Kevin Richardson <kevin@kevinrichardson.co>
Kevin Shi <kshi@andrew.cmu.edu>
Kevin Wallace <kevin@pentabarf.net>
@@ -1146,6 +1171,7 @@ longliqiang88 <394564827@qq.com>
Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
Lorenzo Fontana <fontanalorenz@gmail.com>
Lotus Fenn <fenn.lotus@gmail.com>
Louis Delossantos <ldelossa.ld@gmail.com>
Louis Opter <kalessin@kalessin.fr>
Luca Favatella <luca.favatella@erlang-solutions.com>
Luca Marturana <lucamarturana@gmail.com>
@@ -1154,15 +1180,18 @@ Luca-Bogdan Grigorescu <Luca-Bogdan Grigorescu>
Lucas Chan <lucas-github@lucaschan.com>
Lucas Chi <lucas@teacherspayteachers.com>
Lucas Molas <lmolas@fundacionsadosky.org.ar>
Lucas Silvestre <lukas.silvestre@gmail.com>
Luciano Mores <leslau@gmail.com>
Luis Martínez de Bartolomé Izquierdo <lmartinez@biicode.com>
Luiz Svoboda <luizek@gmail.com>
Lukas Heeren <lukas-heeren@hotmail.com>
Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
lukaspustina <lukas.pustina@centerdevice.com>
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
Luke Marsden <me@lukemarsden.net>
Lyn <energylyn@zju.edu.cn>
Lynda O'Leary <lyndaoleary29@gmail.com>
lzhfromutsc <lzhfromustc@gmail.com>
Lénaïc Huard <lhuard@amadeus.com>
Ma Müller <mueller-ma@users.noreply.github.com>
Ma Shimiao <mashimiao.fnst@cn.fujitsu.com>
@@ -1296,6 +1325,7 @@ Michael Stapelberg <michael+gh@stapelberg.de>
Michael Steinert <mike.steinert@gmail.com>
Michael Thies <michaelthies78@gmail.com>
Michael West <mwest@mdsol.com>
Michael Zhao <michael.zhao@arm.com>
Michal Fojtik <mfojtik@redhat.com>
Michal Gebauer <mishak@mishak.net>
Michal Jemala <michal.jemala@gmail.com>
@@ -1380,6 +1410,7 @@ Neyazul Haque <nuhaque@gmail.com>
Nghia Tran <nghia@google.com>
Niall O'Higgins <niallo@unworkable.org>
Nicholas E. Rabenau <nerab@gmx.at>
Nick Adcock <nick.adcock@docker.com>
Nick DeCoursin <n.decoursin@foodpanda.com>
Nick Irvine <nfirvine@nfirvine.com>
Nick Neisen <nwneisen@gmail.com>
@@ -1418,6 +1449,7 @@ Nuutti Kotivuori <naked@iki.fi>
nzwsch <hi@nzwsch.com>
O.S. Tezer <ostezer@gmail.com>
objectified <objectified@gmail.com>
Odin Ugedal <odin@ugedal.com>
Oguz Bilgic <fisyonet@gmail.com>
Oh Jinkyun <tintypemolly@gmail.com>
Ohad Schneider <ohadschn@users.noreply.github.com>
@@ -1428,6 +1460,7 @@ Oliver Reason <oli@overrateddev.co>
Olivier Gambier <dmp42@users.noreply.github.com>
Olle Jonsson <olle.jonsson@gmail.com>
Olli Janatuinen <olli.janatuinen@gmail.com>
Olly Pomeroy <oppomeroy@gmail.com>
Omri Shiv <Omri.Shiv@teradata.com>
Oriol Francès <oriolfa@gmail.com>
Oskar Niburski <oskarniburski@gmail.com>
@@ -1437,6 +1470,7 @@ Ovidio Mallo <ovidio.mallo@gmail.com>
Panagiotis Moustafellos <pmoust@elastic.co>
Paolo G. Giarrusso <p.giarrusso@gmail.com>
Pascal <pascalgn@users.noreply.github.com>
Pascal Bach <pascal.bach@siemens.com>
Pascal Borreli <pascal@borreli.com>
Pascal Hartig <phartig@rdrei.net>
Patrick Böänziger <patrick.baenziger@bsi-software.com>
@@ -1461,6 +1495,7 @@ Paul Nasrat <pnasrat@gmail.com>
Paul Weaver <pauweave@cisco.com>
Paulo Ribeiro <paigr.io@gmail.com>
Pavel Lobashov <ShockwaveNN@gmail.com>
Pavel Matěja <pavel@verotel.cz>
Pavel Pletenev <cpp.create@gmail.com>
Pavel Pospisil <pospispa@gmail.com>
Pavel Sutyrin <pavel.sutyrin@gmail.com>
@@ -1572,6 +1607,7 @@ Riku Voipio <riku.voipio@linaro.org>
Riley Guerin <rileytg.dev@gmail.com>
Ritesh H Shukla <sritesh@vmware.com>
Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
Rob Gulewich <rgulewich@netflix.com>
Rob Vesse <rvesse@dotnetrdf.org>
Robert Bachmann <rb@robertbachmann.at>
Robert Bittle <guywithnose@gmail.com>
@@ -1580,11 +1616,13 @@ Robert Schneider <mail@shakeme.info>
Robert Stern <lexandro2000@gmail.com>
Robert Terhaar <rterhaar@atlanticdynamic.com>
Robert Wallis <smilingrob@gmail.com>
Robert Wang <robert@arctic.tw>
Roberto G. Hashioka <roberto.hashioka@docker.com>
Roberto Muñoz Fernández <robertomf@gmail.com>
Robin Naundorf <r.naundorf@fh-muenster.de>
Robin Schneider <ypid@riseup.net>
Robin Speekenbrink <robin@kingsquare.nl>
Robin Thoni <robin@rthoni.com>
robpc <rpcann@gmail.com>
Rodolfo Carvalho <rhcarvalho@gmail.com>
Rodrigo Vaz <rodrigo.vaz@gmail.com>
@@ -1618,6 +1656,7 @@ Rozhnov Alexandr <nox73@ya.ru>
Rudolph Gottesheim <r.gottesheim@loot.at>
Rui Cao <ruicao@alauda.io>
Rui Lopes <rgl@ruilopes.com>
Ruilin Li <liruilin4@huawei.com>
Runshen Zhu <runshen.zhu@gmail.com>
Russ Magee <rmagee@gmail.com>
Ryan Abrams <rdabrams@gmail.com>
@@ -1656,6 +1695,7 @@ Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
Sam Neirinck <sam@samneirinck.com>
Sam Reis <sreis@atlassian.com>
Sam Rijs <srijs@airpost.net>
Sam Whited <sam@samwhited.com>
Sambuddha Basu <sambuddhabasu1@gmail.com>
Sami Wagiaalla <swagiaal@redhat.com>
Samuel Andaya <samuel@andaya.net>
@@ -1670,6 +1710,7 @@ sapphiredev <se.imas.kr@gmail.com>
Sargun Dhillon <sargun@netflix.com>
Sascha Andres <sascha.andres@outlook.com>
Sascha Grunert <sgrunert@suse.com>
SataQiu <qiushida@beyondcent.com>
Satnam Singh <satnam@raintown.org>
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
Satoshi Tagomori <tagomoris@gmail.com>
@@ -1718,6 +1759,7 @@ Shijun Qin <qinshijun16@mails.ucas.ac.cn>
Shishir Mahajan <shishir.mahajan@redhat.com>
Shoubhik Bose <sbose78@gmail.com>
Shourya Sarcar <shourya.sarcar@gmail.com>
Shu-Wai Chow <shu-wai.chow@seattlechildrens.org>
shuai-z <zs.broccoli@gmail.com>
Shukui Yang <yangshukui@huawei.com>
Shuwei Hao <haosw@cn.ibm.com>
@@ -1728,6 +1770,7 @@ Silas Sewell <silas@sewell.org>
Silvan Jegen <s.jegen@gmail.com>
Simão Reis <smnrsti@gmail.com>
Simei He <hesimei@zju.edu.cn>
Simon Barendse <simon.barendse@gmail.com>
Simon Eskildsen <sirup@sirupsen.com>
Simon Ferquel <simon.ferquel@docker.com>
Simon Leinen <simon.leinen@gmail.com>
@@ -1736,6 +1779,7 @@ Simon Taranto <simon.taranto@gmail.com>
Simon Vikstrom <pullreq@devsn.se>
Sindhu S <sindhus@live.in>
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
skanehira <sho19921005@gmail.com>
Solganik Alexander <solganik@gmail.com>
Solomon Hykes <solomon@docker.com>
Song Gao <song@gao.io>
@@ -1747,16 +1791,18 @@ Sridatta Thatipamala <sthatipamala@gmail.com>
Sridhar Ratnakumar <sridharr@activestate.com>
Srini Brahmaroutu <srbrahma@us.ibm.com>
Srinivasan Srivatsan <srinivasan.srivatsan@hpe.com>
Staf Wagemakers <staf@wagemakers.be>
Stanislav Bondarenko <stanislav.bondarenko@gmail.com>
Steeve Morin <steeve.morin@gmail.com>
Stefan Berger <stefanb@linux.vnet.ibm.com>
Stefan J. Wernli <swernli@microsoft.com>
Stefan Praszalowicz <stefan@greplin.com>
Stefan S. <tronicum@user.github.com>
Stefan Scherer <scherer_stefan@icloud.com>
Stefan Scherer <stefan.scherer@docker.com>
Stefan Staudenmeyer <doerte@instana.com>
Stefan Weil <sw@weilnetz.de>
Stephan Spindler <shutefan@gmail.com>
Stephen Benjamin <stephen@redhat.com>
Stephen Crosby <stevecrozz@gmail.com>
Stephen Day <stephen.day@docker.com>
Stephen Drake <stephen@xenolith.net>
@@ -1773,10 +1819,12 @@ Steven Iveson <sjiveson@outlook.com>
Steven Merrill <steven.merrill@gmail.com>
Steven Richards <steven@axiomzen.co>
Steven Taylor <steven.taylor@me.com>
Stig Larsson <stig@larsson.dev>
Subhajit Ghosh <isubuz.g@gmail.com>
Sujith Haridasan <sujith.h@gmail.com>
Sun Gengze <690388648@qq.com>
Sun Jianbo <wonderflow.sun@gmail.com>
Sune Keller <sune.keller@gmail.com>
Sunny Gogoi <indiasuny000@gmail.com>
Suryakumar Sudar <surya.trunks@gmail.com>
Sven Dowideit <SvenDowideit@home.org.au>
@@ -1827,6 +1875,7 @@ Tianyi Wang <capkurmagati@gmail.com>
Tibor Vass <teabee89@gmail.com>
Tiffany Jernigan <tiffany.f.j@gmail.com>
Tiffany Low <tiffany@box.com>
Tim <elatllat@gmail.com>
Tim Bart <tim@fewagainstmany.com>
Tim Bosse <taim@bosboot.org>
Tim Dettrick <t.dettrick@uq.edu.au>
@@ -1912,6 +1961,7 @@ Victor Palma <palma.victor@gmail.com>
Victor Vieux <victor.vieux@docker.com>
Victoria Bialas <victoria.bialas@docker.com>
Vijaya Kumar K <vijayak@caviumnetworks.com>
Vikram bir Singh <vikrambir.singh@docker.com>
Viktor Stanchev <me@viktorstanchev.com>
Viktor Vojnovski <viktor.vojnovski@amadeus.com>
VinayRaghavanKS <raghavan.vinay@gmail.com>
@@ -1969,6 +2019,7 @@ Wenyu You <21551128@zju.edu.cn>
Wenzhi Liang <wenzhi.liang@gmail.com>
Wes Morgan <cap10morgan@gmail.com>
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
Wiktor Kwapisiewicz <wiktor@metacode.biz>
Will Dietz <w@wdtz.org>
Will Rouesnel <w.rouesnel@gmail.com>
Will Weaver <monkey@buildingbananas.com>
@@ -1996,6 +2047,7 @@ xichengliudui <1693291525@qq.com>
xiekeyang <xiekeyang@huawei.com>
Ximo Guanter Gonzálbez <joaquin.guantergonzalbez@telefonica.com>
Xinbo Weng <xihuanbo_0521@zju.edu.cn>
Xinfeng Liu <xinfeng.liu@gmail.com>
Xinzi Zhou <imdreamrunner@gmail.com>
Xiuming Chen <cc@cxm.cc>
Xuecong Liao <satorulogic@gmail.com>
@@ -2010,6 +2062,7 @@ Yang Pengfei <yangpengfei4@huawei.com>
yangchenliang <yangchenliang@huawei.com>
Yanqiang Miao <miao.yanqiang@zte.com.cn>
Yao Zaiyong <yaozaiyong@hotmail.com>
Yash Murty <yashmurty@gmail.com>
Yassine Tijani <yasstij11@gmail.com>
Yasunori Mahata <nori@mahata.net>
Yazhong Liu <yorkiefixer@gmail.com>
@@ -2024,6 +2077,7 @@ Yongxin Li <yxli@alauda.io>
Yongzhi Pan <panyongzhi@gmail.com>
Yosef Fertel <yfertel@gmail.com>
You-Sheng Yang (楊有勝) <vicamo@gmail.com>
youcai <omegacoleman@gmail.com>
Youcef YEKHLEF <yyekhlef@gmail.com>
Yu Changchun <yuchangchun1@huawei.com>
Yu Chengxia <yuchengxia@huawei.com>
@@ -2060,6 +2114,7 @@ Zhoulin Xie <zhoulin.xie@daocloud.io>
Zhu Guihua <zhugh.fnst@cn.fujitsu.com>
Zhu Kunjia <zhu.kunjia@zte.com.cn>
Zhuoyun Wei <wzyboy@wzyboy.org>
Ziheng Liu <lzhfromustc@gmail.com>
Zilin Du <zilin.du@gmail.com>
zimbatm <zimbatm@zimbatm.com>
Ziming Dong <bnudzm@foxmail.com>
@@ -2068,7 +2123,7 @@ zmarouf <zeid.marouf@gmail.com>
Zoltan Tombol <zoltan.tombol@gmail.com>
Zou Yu <zouyu7@huawei.com>
zqh <zqhxuyuan@gmail.com>
Zuhayr Elahi <elahi.zuhayr@gmail.com>
Zuhayr Elahi <zuhayr.elahi@docker.com>
Zunayed Ali <zunayed@gmail.com>
Álex González <agonzalezro@gmail.com>
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>

View File

@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client.
const (
// DefaultVersion of Current REST API
DefaultVersion = "1.40"
DefaultVersion = "1.41"
// NoBaseImageSpecifier is the symbol used by the FROM
// command to specify that no base image is to be used.

View File

@@ -19,10 +19,10 @@ produces:
consumes:
- "application/json"
- "text/plain"
basePath: "/v1.40"
basePath: "/v1.41"
info:
title: "Docker Engine API"
version: "1.40"
version: "1.41"
x-logo:
url: "https://docs.docker.com/images/logo-docker-main.png"
description: |
@@ -49,8 +49,8 @@ info:
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
is returned.
If you omit the version-prefix, the current version of the API (v1.40) is used.
For example, calling `/info` is the same as calling `/v1.40/info`. Using the
If you omit the version-prefix, the current version of the API (v1.41) is used.
For example, calling `/info` is the same as calling `/v1.41/info`. Using the
API without a version-prefix is deprecated and will be removed in a future release.
Engine releases in the near future should support this version of the API,
@@ -65,7 +65,7 @@ info:
# Authentication
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) (JSON) string with the following structure:
Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure:
```
{
@@ -618,71 +618,6 @@ definitions:
description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit."
type: "integer"
Health:
description: |
Health stores information about the container's healthcheck results.
type: "object"
properties:
Status:
description: |
Status is one of `none`, `starting`, `healthy` or `unhealthy`
- "none" Indicates there is no healthcheck
- "starting" Starting indicates that the container is not yet ready
- "healthy" Healthy indicates that the container is running correctly
- "unhealthy" Unhealthy indicates that the container has a problem
type: "string"
enum:
- "none"
- "starting"
- "healthy"
- "unhealthy"
example: "healthy"
FailingStreak:
description: "FailingStreak is the number of consecutive failures"
type: "integer"
example: 0
Log:
type: "array"
description: |
Log contains the last few results (oldest first)
items:
x-nullable: true
$ref: "#/definitions/HealthcheckResult"
HealthcheckResult:
description: |
HealthcheckResult stores information about a single run of a healthcheck probe
type: "object"
properties:
Start:
description: |
Date and time at which this check started in
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
type: "string"
format: "date-time"
example: "2020-01-04T10:44:24.496525531Z"
End:
description: |
Date and time at which this check ended in
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
type: "string"
format: "dateTime"
example: "2020-01-04T10:45:21.364524523Z"
ExitCode:
description: |
ExitCode meanings:
- `0` healthy
- `1` unhealthy
- `2` reserved (considered unhealthy)
- other values: error running probe
type: "integer"
example: 0
Output:
description: "Output from last check"
type: "string"
HostConfig:
description: "Container configuration that depends on the host we are running on"
allOf:
@@ -693,44 +628,12 @@ definitions:
Binds:
type: "array"
description: |
A list of volume bindings for this container. Each volume binding
is a string in one of these forms:
A list of volume bindings for this container. Each volume binding is a string in one of these forms:
- `host-src:container-dest[:options]` to bind-mount a host path
into the container. Both `host-src`, and `container-dest` must
be an _absolute_ path.
- `volume-name:container-dest[:options]` to bind-mount a volume
managed by a volume driver into the container. `container-dest`
must be an _absolute_ path.
`options` is an optional, comma-delimited list of:
- `nocopy` disables automatic copying of data from the container
path to the volume. The `nocopy` flag only applies to named volumes.
- `[ro|rw]` mounts a volume read-only or read-write, respectively.
If omitted or set to `rw`, volumes are mounted read-write.
- `[z|Z]` applies SELinux labels to allow or deny multiple containers
to read and write to the same volume.
- `z`: a _shared_ content label is applied to the content. This
label indicates that multiple containers can share the volume
content, for both reading and writing.
- `Z`: a _private unshared_ label is applied to the content.
This label indicates that only the current container can use
a private volume. Labeling systems such as SELinux require
proper labels to be placed on volume content that is mounted
into a container. Without a label, the security system can
prevent a container's processes from using the content. By
default, the labels set by the host operating system are not
modified.
- `[[r]shared|[r]slave|[r]private]` specifies mount
[propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt).
This only applies to bind-mounted volumes, not internal volumes
or named volumes. Mount propagation requires the source mount
point (the location where the source directory is mounted in the
host operating system) to have the correct propagation properties.
For shared volumes, the source mount point must be set to `shared`.
For slave volumes, the mount must be set to either `shared` or
`slave`.
- `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path.
- `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path.
- `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path.
- `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path.
items:
type: "string"
ContainerIDFile:
@@ -800,6 +703,19 @@ definitions:
description: "A list of kernel capabilities to drop from the container. Conflicts with option 'Capabilities'"
items:
type: "string"
CgroupnsMode:
type: "string"
enum:
- "private"
- "host"
description: |
cgroup namespace mode for the container. Possible values are:
- `"private"`: the container runs in its own private cgroup namespace
- `"host"`: use the host system's cgroup namespace
If not specified, the daemon default is used, which can either be `"private"`
or `"host"`, depending on daemon version, kernel support and configuration.
Dns:
type: "array"
description: "A list of DNS servers for the container to use."
@@ -2966,6 +2882,18 @@ definitions:
type: "object"
additionalProperties:
type: "string"
# This option is not used by Windows containers
Capabilities:
type: "array"
description: |
A list of kernel capabilities to be available for container (this overrides the default set).
items:
type: "string"
example:
- "CAP_NET_RAW"
- "CAP_SYS_ADMIN"
- "CAP_SYS_CHROOT"
- "CAP_SYSLOG"
NetworkAttachmentSpec:
description: |
Read-only spec type for non-swarm containers attached to swarm overlay
@@ -3021,27 +2949,7 @@ definitions:
type: "object"
properties:
Constraints:
description: |
An array of constraint expressions to limit the set of nodes where
a task can be scheduled. Constraint expressions can either use a
_match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find
nodes that satisfy every expression (AND match). Constraints can
match node or Docker Engine labels as follows:
node attribute | matches | example
---------------------|--------------------------------|-----------------------------------------------
`node.id` | Node ID | `node.id==2ivku8v2gvtg4`
`node.hostname` | Node hostname | `node.hostname!=node-2`
`node.role` | Node role (`manager`/`worker`) | `node.role==manager`
`node.platform.os` | Node operating system | `node.platform.os==windows`
`node.platform.arch` | Node architecture | `node.platform.arch==x86_64`
`node.labels` | User-defined node labels | `node.labels.security==high`
`engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04`
`engine.labels` apply to Docker Engine labels like operating system,
drivers, etc. Swarm administrators add `node.labels` for operational
purposes by using the [`node update endpoint`](#operation/NodeUpdate).
description: "An array of constraints."
type: "array"
items:
type: "string"
@@ -3049,8 +2957,6 @@ definitions:
- "node.hostname!=node3.corp.example.com"
- "node.role!=manager"
- "node.labels.type==production"
- "node.platform.os==linux"
- "node.platform.arch==x86_64"
Preferences:
description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence."
type: "array"
@@ -3451,6 +3357,27 @@ definitions:
format: "dateTime"
Message:
type: "string"
ServiceStatus:
description: |
The status of the service's tasks. Provided only when requested as
part of a ServiceList operation.
type: "object"
properties:
RunningTasks:
description: "The number of tasks for the service currently in the Running state"
type: "integer"
format: "uint64"
example: 7
DesiredTasks:
description: |
The number of tasks for the service desired to be running.
For replicated services, this is the replica count from the
service spec. For global services, this is computed by taking
count of all tasks for the service with a Desired State other
than Shutdown.
type: "integer"
format: "uint64"
example: 10
example:
ID: "9mnpnzenvg8p8tdbtq4wvbkcz"
Version:
@@ -3639,7 +3566,7 @@ definitions:
com.example.some-other-label: "some-other-value"
Data:
description: |
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2))
data to store as secret.
This field is only used to _create_ a secret, and is not returned by
@@ -3689,7 +3616,7 @@ definitions:
type: "string"
Data:
description: |
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2))
config data.
type: "string"
Templating:
@@ -3716,70 +3643,6 @@ definitions:
Spec:
$ref: "#/definitions/ConfigSpec"
ContainerState:
description: |
ContainerState stores container's running state. It's part of ContainerJSONBase
and will be returned by the "inspect" command.
type: "object"
properties:
Status:
description: |
String representation of the container state. Can be one of "created",
"running", "paused", "restarting", "removing", "exited", or "dead".
type: "string"
enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"]
example: "running"
Running:
description: |
Whether this container is running.
Note that a running container can be _paused_. The `Running` and `Paused`
booleans are not mutually exclusive:
When pausing a container (on Linux), the freezer cgroup is used to suspend
all processes in the container. Freezing the process requires the process to
be running. As a result, paused containers are both `Running` _and_ `Paused`.
Use the `Status` field instead to determine if a container's state is "running".
type: "boolean"
example: true
Paused:
description: "Whether this container is paused."
type: "boolean"
example: false
Restarting:
description: "Whether this container is restarting."
type: "boolean"
example: false
OOMKilled:
description: "Whether this container has been killed because it ran out of memory."
type: "boolean"
example: false
Dead:
type: "boolean"
example: false
Pid:
description: "The process ID of this container"
type: "integer"
example: 1234
ExitCode:
description: "The last exit code of this container"
type: "integer"
example: 0
Error:
type: "string"
StartedAt:
description: "The time when this container was last started."
type: "string"
example: "2020-01-06T09:06:59.461876391Z"
FinishedAt:
description: "The time when this container last exited."
type: "string"
example: "2020-01-06T09:07:59.461876391Z"
Health:
x-nullable: true
$ref: "#/definitions/Health"
SystemInfo:
type: "object"
properties:
@@ -3998,6 +3861,17 @@ definitions:
or "Windows Server 2016 Datacenter"
type: "string"
example: "Alpine Linux v3.5"
OSVersion:
description: |
Version of the host's operating system
<p><br /></p>
> **Note**: The information returned in this field, including its
> very existence, and the formatting of values, should not be considered
> stable, and may change without notice.
type: "string"
example: "16.04"
OSType:
description: |
Generic type of the operating system of the host, as returned by the
@@ -5011,8 +4885,52 @@ paths:
items:
type: "string"
State:
x-nullable: true
$ref: "#/definitions/ContainerState"
description: "The state of the container."
type: "object"
properties:
Status:
description: |
The status of the container. For example, `"running"` or `"exited"`.
type: "string"
enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"]
Running:
description: |
Whether this container is running.
Note that a running container can be _paused_. The `Running` and `Paused`
booleans are not mutually exclusive:
When pausing a container (on Linux), the freezer cgroup is used to suspend
all processes in the container. Freezing the process requires the process to
be running. As a result, paused containers are both `Running` _and_ `Paused`.
Use the `Status` field instead to determine if a container's state is "running".
type: "boolean"
Paused:
description: "Whether this container is paused."
type: "boolean"
Restarting:
description: "Whether this container is restarting."
type: "boolean"
OOMKilled:
description: "Whether this container has been killed because it ran out of memory."
type: "boolean"
Dead:
type: "boolean"
Pid:
description: "The process ID of this container"
type: "integer"
ExitCode:
description: "The last exit code of this container"
type: "integer"
Error:
type: "string"
StartedAt:
description: "The time when this container was last started."
type: "string"
FinishedAt:
description: "The time when this container last exited."
type: "string"
Image:
description: "The container's image"
type: "string"
@@ -5084,8 +5002,6 @@ paths:
Domainname: ""
Env:
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Healthcheck:
Test: ["CMD-SHELL", "exit 0"]
Hostname: "ba033ac44011"
Image: "ubuntu"
Labels:
@@ -5197,14 +5113,6 @@ paths:
Error: ""
ExitCode: 9
FinishedAt: "2015-01-06T15:47:32.080254511Z"
Health:
Status: "healthy"
FailingStreak: 0
Log:
- Start: "2019-12-22T10:59:05.6385933Z"
End: "2019-12-22T10:59:05.8078452Z"
ExitCode: 0
Output: ""
OOMKilled: false
Dead: false
Paused: false
@@ -6759,10 +6667,6 @@ paths:
in: "query"
description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled."
type: "string"
- name: "message"
in: "query"
description: "Set commit message for imported image."
type: "string"
- name: "inputImage"
in: "body"
description: "Image content if the value `-` has been specified in fromSrc query parameter"
@@ -6771,7 +6675,7 @@ paths:
required: false
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
type: "string"
- name: "platform"
in: "query"
@@ -6999,7 +6903,7 @@ paths:
type: "string"
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
type: "string"
required: true
tags: ["Image"]
@@ -8705,7 +8609,7 @@ paths:
type: "string"
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)"
type: "string"
- name: "body"
in: "body"
@@ -8870,7 +8774,7 @@ paths:
type: "string"
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)"
type: "string"
- name: "body"
in: "body"
@@ -9441,6 +9345,10 @@ paths:
- `label=<service label>`
- `mode=["replicated"|"global"]`
- `name=<service name>`
- name: "status"
in: "query"
type: "boolean"
description: "Include service status, with count of running and desired tasks"
tags: ["Service"]
/services/create:
post:
@@ -9563,7 +9471,7 @@ paths:
foo: "bar"
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
type: "string"
tags: ["Service"]
/services/{id}:
@@ -9722,7 +9630,7 @@ paths:
type: "string"
- name: "X-Registry-Auth"
in: "header"
description: "A base64url-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
type: "string"
tags: ["Service"]

View File

@@ -50,7 +50,7 @@ type ContainerCommitOptions struct {
// ContainerExecInspect holds information returned by exec inspect.
type ContainerExecInspect struct {
ExecID string `json:"ID"`
ExecID string
ContainerID string
Running bool
ExitCode int
@@ -363,6 +363,10 @@ type ServiceUpdateOptions struct {
// ServiceListOptions holds parameters to list services with.
type ServiceListOptions struct {
Filters filters.Args
// Status indicates whether the server should include the service task
// count of running and desired tasks.
Status bool
}
// ServiceInspectOptions holds parameters related to the "service inspect"

View File

@@ -1,8 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -7,9 +7,32 @@ import (
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
units "github.com/docker/go-units"
)
// CgroupnsMode represents the cgroup namespace mode of the container
type CgroupnsMode string
// IsPrivate indicates whether the container uses its own private cgroup namespace
func (c CgroupnsMode) IsPrivate() bool {
return c == "private"
}
// IsHost indicates whether the container shares the host's cgroup namespace
func (c CgroupnsMode) IsHost() bool {
return c == "host"
}
// IsEmpty indicates whether the container cgroup namespace mode is unset
func (c CgroupnsMode) IsEmpty() bool {
return c == ""
}
// Valid indicates whether the cgroup namespace mode is valid
func (c CgroupnsMode) Valid() bool {
return c.IsEmpty() || c.IsPrivate() || c.IsHost()
}
// Isolation represents the isolation technology of a container. The supported
// values are platform specific
type Isolation string
@@ -381,9 +404,10 @@ type HostConfig struct {
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set)
DNS []string `json:"Dns"` // List of DNS server to lookup
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
DNS []string `json:"Dns"` // List of DNS server to lookup
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
ExtraHosts []string // List of extra hosts
GroupAdd []string // List of additional groups that the container process will run as
IpcMode IpcMode // IPC namespace to use for the container

View File

@@ -0,0 +1,6 @@
package types
// Error returns the error message
func (e ErrorResponse) Error() string {
return e.Message
}

View File

@@ -66,7 +66,7 @@ func ToJSON(a Args) (string, error) {
// then the encoded format will use an older legacy format where the values are a
// list of strings, instead of a set.
//
// Deprecated: Use ToJSON
// Deprecated: do not use in any new code; use ToJSON instead
func ToParamWithVersion(version string, a Args) (string, error) {
if a.Len() == 0 {
return "", nil

View File

@@ -1,8 +1,7 @@
package image // import "github.com/docker/docker/api/types/image"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
"net"
"github.com/opencontainers/image-spec/specs-go/v1"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
// ServiceConfig stores daemon registry services configuration.

View File

@@ -67,10 +67,11 @@ type ContainerSpec struct {
// The format of extra hosts on swarmkit is specified in:
// http://man7.org/linux/man-pages/man5/hosts.5.html
// IP_address canonical_hostname [aliases...]
Hosts []string `json:",omitempty"`
DNSConfig *DNSConfig `json:",omitempty"`
Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"`
Sysctls map[string]string `json:",omitempty"`
Hosts []string `json:",omitempty"`
DNSConfig *DNSConfig `json:",omitempty"`
Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"`
Sysctls map[string]string `json:",omitempty"`
Capabilities []string `json:",omitempty"`
}

View File

@@ -1,6 +1,5 @@
// Code generated by protoc-gen-gogo.
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: plugin.proto
// DO NOT EDIT!
/*
Package runtime is a generated protocol buffer package.
@@ -38,6 +37,7 @@ type PluginSpec struct {
Remote string `protobuf:"bytes,2,opt,name=remote,proto3" json:"remote,omitempty"`
Privileges []*PluginPrivilege `protobuf:"bytes,3,rep,name=privileges" json:"privileges,omitempty"`
Disabled bool `protobuf:"varint,4,opt,name=disabled,proto3" json:"disabled,omitempty"`
Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"`
}
func (m *PluginSpec) Reset() { *m = PluginSpec{} }
@@ -73,6 +73,13 @@ func (m *PluginSpec) GetDisabled() bool {
return false
}
func (m *PluginSpec) GetEnv() []string {
if m != nil {
return m.Env
}
return nil
}
// PluginPrivilege describes a permission the user has to accept
// upon installing a plugin.
type PluginPrivilege struct {
@@ -160,6 +167,21 @@ func (m *PluginSpec) MarshalTo(dAtA []byte) (int, error) {
}
i++
}
if len(m.Env) > 0 {
for _, s := range m.Env {
dAtA[i] = 0x2a
i++
l = len(s)
for l >= 1<<7 {
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
l >>= 7
i++
}
dAtA[i] = uint8(l)
i++
i += copy(dAtA[i:], s)
}
}
return i, nil
}
@@ -208,24 +230,6 @@ func (m *PluginPrivilege) MarshalTo(dAtA []byte) (int, error) {
return i, nil
}
func encodeFixed64Plugin(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
dAtA[offset+4] = uint8(v >> 32)
dAtA[offset+5] = uint8(v >> 40)
dAtA[offset+6] = uint8(v >> 48)
dAtA[offset+7] = uint8(v >> 56)
return offset + 8
}
func encodeFixed32Plugin(dAtA []byte, offset int, v uint32) int {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
dAtA[offset+2] = uint8(v >> 16)
dAtA[offset+3] = uint8(v >> 24)
return offset + 4
}
func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
@@ -255,6 +259,12 @@ func (m *PluginSpec) Size() (n int) {
if m.Disabled {
n += 2
}
if len(m.Env) > 0 {
for _, s := range m.Env {
l = len(s)
n += 1 + l + sovPlugin(uint64(l))
}
}
return n
}
@@ -429,6 +439,35 @@ func (m *PluginSpec) Unmarshal(dAtA []byte) error {
}
}
m.Disabled = bool(v != 0)
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Env = append(m.Env, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPlugin(dAtA[iNdEx:])
@@ -695,18 +734,21 @@ var (
func init() { proto.RegisterFile("plugin.proto", fileDescriptorPlugin) }
var fileDescriptorPlugin = []byte{
// 196 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0xc8, 0x29, 0x4d,
0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x6a, 0x63, 0xe4, 0xe2, 0x0a, 0x00, 0x0b,
0x04, 0x17, 0xa4, 0x26, 0x0b, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30,
0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x62, 0x5c, 0x6c, 0x45, 0xa9, 0xb9, 0xf9, 0x25, 0xa9, 0x12,
0x4c, 0x60, 0x51, 0x28, 0x4f, 0xc8, 0x80, 0x8b, 0xab, 0xa0, 0x28, 0xb3, 0x2c, 0x33, 0x27, 0x35,
0x3d, 0xb5, 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x40, 0x0f, 0x62, 0x58, 0x00, 0x4c,
0x22, 0x08, 0x49, 0x8d, 0x90, 0x14, 0x17, 0x47, 0x4a, 0x66, 0x71, 0x62, 0x52, 0x4e, 0x6a, 0x8a,
0x04, 0x8b, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x9c, 0xaf, 0x14, 0xcb, 0xc5, 0x8f, 0xa6, 0x15, 0xab,
0x63, 0x14, 0xb8, 0xb8, 0x53, 0x52, 0x8b, 0x93, 0x8b, 0x32, 0x0b, 0x4a, 0x32, 0xf3, 0xf3, 0xa0,
0x2e, 0x42, 0x16, 0x12, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x05, 0xbb, 0x88, 0x33,
0x08, 0xc2, 0x71, 0xe2, 0x39, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4,
0x18, 0x93, 0xd8, 0xc0, 0x9e, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x84, 0xad, 0x79,
0x0c, 0x01, 0x00, 0x00,
// 256 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4d, 0x4b, 0xc3, 0x30,
0x18, 0xc7, 0x89, 0xdd, 0xc6, 0xfa, 0x4c, 0x70, 0x04, 0x91, 0xe2, 0xa1, 0x94, 0x9d, 0x7a, 0x6a,
0x45, 0x2f, 0x82, 0x37, 0x0f, 0x9e, 0x47, 0xbc, 0x09, 0x1e, 0xd2, 0xf6, 0xa1, 0x06, 0x9b, 0x17,
0x92, 0xb4, 0xe2, 0x37, 0xf1, 0x23, 0x79, 0xf4, 0x23, 0x48, 0x3f, 0x89, 0x98, 0x75, 0x32, 0x64,
0xa7, 0xff, 0x4b, 0xc2, 0x9f, 0x1f, 0x0f, 0x9c, 0x9a, 0xae, 0x6f, 0x85, 0x2a, 0x8c, 0xd5, 0x5e,
0x6f, 0x3e, 0x08, 0xc0, 0x36, 0x14, 0x8f, 0x06, 0x6b, 0x4a, 0x61, 0xa6, 0xb8, 0xc4, 0x84, 0x64,
0x24, 0x8f, 0x59, 0xf0, 0xf4, 0x02, 0x16, 0x16, 0xa5, 0xf6, 0x98, 0x9c, 0x84, 0x76, 0x4a, 0xf4,
0x0a, 0xc0, 0x58, 0x31, 0x88, 0x0e, 0x5b, 0x74, 0x49, 0x94, 0x45, 0xf9, 0xea, 0x7a, 0x5d, 0xec,
0xc6, 0xb6, 0xfb, 0x07, 0x76, 0xf0, 0x87, 0x5e, 0xc2, 0xb2, 0x11, 0x8e, 0x57, 0x1d, 0x36, 0xc9,
0x2c, 0x23, 0xf9, 0x92, 0xfd, 0x65, 0xba, 0x86, 0x08, 0xd5, 0x90, 0xcc, 0xb3, 0x28, 0x8f, 0xd9,
0xaf, 0xdd, 0x3c, 0xc3, 0xd9, 0xbf, 0xb1, 0xa3, 0x78, 0x19, 0xac, 0x1a, 0x74, 0xb5, 0x15, 0xc6,
0x0b, 0xad, 0x26, 0xc6, 0xc3, 0x8a, 0x9e, 0xc3, 0x7c, 0xe0, 0x5d, 0x8f, 0x81, 0x31, 0x66, 0xbb,
0x70, 0xff, 0xf0, 0x39, 0xa6, 0xe4, 0x6b, 0x4c, 0xc9, 0xf7, 0x98, 0x92, 0xa7, 0xdb, 0x56, 0xf8,
0x97, 0xbe, 0x2a, 0x6a, 0x2d, 0xcb, 0x46, 0xd7, 0xaf, 0x68, 0xf7, 0xc2, 0x8d, 0x28, 0xfd, 0xbb,
0x41, 0x57, 0xba, 0x37, 0x6e, 0x65, 0x69, 0x7b, 0xe5, 0x85, 0xc4, 0xbb, 0x49, 0xab, 0x45, 0x38,
0xe4, 0xcd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xa8, 0xd9, 0x9b, 0x58, 0x01, 0x00, 0x00,
}

View File

@@ -9,6 +9,7 @@ message PluginSpec {
string remote = 2;
repeated PluginPrivilege privileges = 3;
bool disabled = 4;
repeated string env = 5;
}
// PluginPrivilege describes a permission the user has to accept

View File

@@ -10,6 +10,13 @@ type Service struct {
PreviousSpec *ServiceSpec `json:",omitempty"`
Endpoint Endpoint `json:",omitempty"`
UpdateStatus *UpdateStatus `json:",omitempty"`
// ServiceStatus is an optional, extra field indicating the number of
// desired and running tasks. It is provided primarily as a shortcut to
// calculating these values client-side, which otherwise would require
// listing all tasks for a service, an operation that could be
// computation and network expensive.
ServiceStatus *ServiceStatus `json:",omitempty"`
}
// ServiceSpec represents the spec of a service.
@@ -122,3 +129,17 @@ type UpdateConfig struct {
// started, or the new task is started before the old task is shut down.
Order string
}
// ServiceStatus represents the number of running tasks in a service and the
// number of tasks desired to be running.
type ServiceStatus struct {
// RunningTasks is the number of tasks for the service actually in the
// Running state
RunningTasks uint64
// DesiredTasks is the number of tasks desired to be running by the
// service. For replicated services, this is the replica count. For global
// services, this is computed by taking the number of tasks with desired
// state of not-Shutdown.
DesiredTasks uint64
}

View File

@@ -39,6 +39,7 @@ type ImageInspect struct {
Author string
Config *container.Config
Architecture string
Variant string `json:",omitempty"`
Os string
OsVersion string `json:",omitempty"`
Size int64
@@ -177,6 +178,7 @@ type Info struct {
NEventsListener int
KernelVersion string
OperatingSystem string
OSVersion string
OSType string
Architecture string
IndexServerAddress string

View File

@@ -1,8 +1,7 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -1,8 +1,7 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE
// This file was generated by `swagger generate operation`
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------

View File

@@ -252,7 +252,8 @@ func (cli *Client) DaemonHost() string {
// HTTPClient returns a copy of the HTTP client bound to the server
func (cli *Client) HTTPClient() *http.Client {
return &*cli.client
c := *cli.client
return &c
}
// ParseHostURL parses a url string, validates the string is a host url, and

View File

@@ -35,6 +35,7 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
}
if options.Filters.Len() > 0 {
//nolint:staticcheck // ignore SA1019 for old code
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
if err != nil {

View File

@@ -90,6 +90,7 @@ func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url
}
if options.Filters.Len() > 0 {
//nolint:staticcheck // ignore SA1019 for old code
filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters)
if err != nil {
return nil, err

View File

@@ -24,7 +24,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
}
apiPath := cli.getAPIPath(ctx, path, query)
req, err := http.NewRequest("POST", apiPath, bodyEncoded)
req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded)
if err != nil {
return types.HijackedResponse{}, err
}
@@ -40,7 +40,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
// DialHijack returns a hijacked connection with negotiated protocol proto.
func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) {
req, err := http.NewRequest("POST", url, nil)
req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
return nil, err
}
@@ -87,6 +87,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
// Server hijacks the connection, error 'connection closed' expected
resp, err := clientconn.Do(req)
//nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons
if err != httputil.ErrPersistEOF {
if err != nil {
return nil, err

View File

@@ -24,6 +24,7 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
}
}
if optionFilters.Len() > 0 {
//nolint:staticcheck // ignore SA1019 for old code
filterJSON, err := filters.ToParamWithVersion(cli.version, optionFilters)
if err != nil {
return images, err

Some files were not shown because too many files have changed in this diff Show More