diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go
index 25c0a32b2..b68f7f2a7 100644
--- a/api/v1alpha1/clusterextension_types.go
+++ b/api/v1alpha1/clusterextension_types.go
@@ -470,8 +470,6 @@ type BundleMetadata struct {
type ClusterExtensionStatus struct {
Install *ClusterExtensionInstallStatus `json:"install,omitempty"`
- Resolution *ClusterExtensionResolutionStatus `json:"resolution,omitempty"`
-
// conditions is a representation of the current state for this ClusterExtension.
// The status is represented by a set of "conditions".
//
@@ -512,16 +510,6 @@ type ClusterExtensionInstallStatus struct {
Bundle BundleMetadata `json:"bundle"`
}
-type ClusterExtensionResolutionStatus struct {
- // bundle is a representation of the bundle that was identified during
- // resolution to meet all installation/upgrade constraints and is slated to be
- // installed or upgraded to.
- //
- // A "bundle" is a versioned set of content that represents the resources that
- // need to be applied to a cluster to install a package.
- Bundle BundleMetadata `json:"bundle"`
-}
-
//+kubebuilder:object:root=true
//+kubebuilder:resource:scope=Cluster
//+kubebuilder:subresource:status
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index ed65d696b..ccd143aec 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -172,22 +172,6 @@ func (in *ClusterExtensionList) DeepCopyObject() runtime.Object {
return nil
}
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ClusterExtensionResolutionStatus) DeepCopyInto(out *ClusterExtensionResolutionStatus) {
- *out = *in
- out.Bundle = in.Bundle
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionResolutionStatus.
-func (in *ClusterExtensionResolutionStatus) DeepCopy() *ClusterExtensionResolutionStatus {
- if in == nil {
- return nil
- }
- out := new(ClusterExtensionResolutionStatus)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) {
*out = *in
@@ -213,11 +197,6 @@ func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) {
*out = new(ClusterExtensionInstallStatus)
**out = **in
}
- if in.Resolution != nil {
- in, out := &in.Resolution, &out.Resolution
- *out = new(ClusterExtensionResolutionStatus)
- **out = **in
- }
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
diff --git a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml
index c25c2ccb2..f24871f00 100644
--- a/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml
+++ b/config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml
@@ -572,34 +572,6 @@ spec:
required:
- bundle
type: object
- resolution:
- properties:
- bundle:
- description: |-
- bundle is a representation of the bundle that was identified during
- resolution to meet all installation/upgrade constraints and is slated to be
- installed or upgraded to.
-
- A "bundle" is a versioned set of content that represents the resources that
- need to be applied to a cluster to install a package.
- properties:
- name:
- description: |-
- name is a required field and is a reference
- to the name of a bundle
- type: string
- version:
- description: |-
- version is a required field and is a reference
- to the version that this bundle represents
- type: string
- required:
- - name
- - version
- type: object
- required:
- - bundle
- type: object
type: object
type: object
served: true
diff --git a/docs/refs/api/operator-controller-api-reference.md b/docs/refs/api/operator-controller-api-reference.md
index de0a1f2bd..0c5aced18 100644
--- a/docs/refs/api/operator-controller-api-reference.md
+++ b/docs/refs/api/operator-controller-api-reference.md
@@ -24,7 +24,6 @@ Package v1alpha1 contains API Schema definitions for the olm v1alpha1 API group
_Appears in:_
- [ClusterExtensionInstallStatus](#clusterextensioninstallstatus)
-- [ClusterExtensionResolutionStatus](#clusterextensionresolutionstatus)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
@@ -162,22 +161,6 @@ ClusterExtensionList contains a list of ClusterExtension
| `items` _[ClusterExtension](#clusterextension) array_ | | | |
-#### ClusterExtensionResolutionStatus
-
-
-
-
-
-
-
-_Appears in:_
-- [ClusterExtensionStatus](#clusterextensionstatus)
-
-| Field | Description | Default | Validation |
-| --- | --- | --- | --- |
-| `bundle` _[BundleMetadata](#bundlemetadata)_ | bundle is a representation of the bundle that was identified during
resolution to meet all installation/upgrade constraints and is slated to be
installed or upgraded to.
A "bundle" is a versioned set of content that represents the resources that
need to be applied to a cluster to install a package. | | |
-
-
#### ClusterExtensionSpec
@@ -209,7 +192,6 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | | | |
-| `resolution` _[ClusterExtensionResolutionStatus](#clusterextensionresolutionstatus)_ | | | |
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions is a representation of the current state for this ClusterExtension.
The status is represented by a set of "conditions".
Each condition is generally structured in the following format:
- Type: a string representation of the condition type. More or less the condition "name".
- Status: a string representation of the state of the condition. Can be one of ["True", "False", "Unknown"].
- Reason: a string representation of the reason for the current state of the condition. Typically useful for building automation around particular Type+Reason combinations.
- Message: a human readable message that further elaborates on the state of the condition
The global set of condition types are:
- "Installed", represents whether or not the a bundle has been installed for this ClusterExtension
- "Resolved", represents whether or not a bundle was found that satisfies the selection criteria outlined in the spec
- "Unpacked", represents whether or not the bundle contents have been successfully unpacked
When the ClusterExtension is sourced from a catalog, the following conditions are also possible:
- "Deprecated", represents an aggregation of the PackageDeprecated, ChannelDeprecated, and BundleDeprecated condition types
- "PackageDeprecated", represents whether or not the package specified in the spec.source.catalog.packageName field has been deprecated
- "ChannelDeprecated", represents whether or not any channel specified in spec.source.catalog.channels has been deprecated
- "BundleDeprecated", represents whether or not the installed bundle is deprecated
The current set of reasons are:
- "Success", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when unpacking a bundle's content, resolution and installation/upgrading is successful
- "Failed", this reason is set on the "Unpacked", "Resolved" and "Installed" conditions when an error has occurred while unpacking the contents of a bundle, during resolution or installation. | | |
diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go
index 4f30f3aa0..65cb2eaf6 100644
--- a/internal/controllers/clusterextension_controller.go
+++ b/internal/controllers/clusterextension_controller.go
@@ -192,9 +192,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
l.Info("handling finalizers")
finalizeResult, err := r.Finalizers.Finalize(ctx, ext)
if err != nil {
- setResolutionStatus(ext, nil)
setStatusProgressing(ext, err)
-
return ctrl.Result{}, err
}
if finalizeResult.Updated || finalizeResult.StatusUpdated {
@@ -219,7 +217,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
if err != nil {
// Note: We don't distinguish between resolution-specific errors and generic errors
setInstallStatus(ext, nil)
- setResolutionStatus(ext, nil)
setStatusProgressing(ext, err)
ensureAllConditionsWithReason(ext, ocv1alpha1.ReasonFailed, err.Error())
return ctrl.Result{}, err
@@ -242,11 +239,6 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
SetDeprecationStatus(ext, resolvedBundle.Name, resolvedDeprecation)
resolvedBundleMetadata := bundleutil.MetadataFor(resolvedBundle.Name, *resolvedBundleVersion)
- resStatus := &ocv1alpha1.ClusterExtensionResolutionStatus{
- Bundle: resolvedBundleMetadata,
- }
- setResolutionStatus(ext, resStatus)
-
bundleSource := &rukpaksource.BundleSource{
Name: ext.GetName(),
Type: rukpaksource.SourceTypeImage,
diff --git a/internal/controllers/clusterextension_controller_test.go b/internal/controllers/clusterextension_controller_test.go
index bf0aeaa19..3bbad3b10 100644
--- a/internal/controllers/clusterextension_controller_test.go
+++ b/internal/controllers/clusterextension_controller_test.go
@@ -82,7 +82,6 @@ func TestClusterExtensionResolutionFails(t *testing.T) {
require.NoError(t, cl.Get(ctx, extKey, clusterExtension))
t.Log("By checking the status fields")
- require.Empty(t, clusterExtension.Status.Resolution)
require.Empty(t, clusterExtension.Status.Install)
t.Log("By checking the expected conditions")
@@ -175,7 +174,6 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) {
t.Log("By checking the status fields")
expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}
- require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle)
require.Empty(t, clusterExtension.Status.Install)
t.Log("By checking the expected conditions")
@@ -319,7 +317,6 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T)
t.Log("By checking the status fields")
expectedBundleMetadata := ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}
- require.Equal(t, expectedBundleMetadata, clusterExtension.Status.Resolution.Bundle)
require.Empty(t, clusterExtension.Status.Install)
t.Log("By checking the expected installed conditions")
@@ -503,7 +500,6 @@ func TestClusterExtensionManagerFailed(t *testing.T) {
require.NoError(t, cl.Get(ctx, extKey, clusterExtension))
t.Log("By checking the status fields")
- require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle)
require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle)
t.Log("By checking the expected installed conditions")
@@ -590,7 +586,6 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) {
require.NoError(t, cl.Get(ctx, extKey, clusterExtension))
t.Log("By checking the status fields")
- require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle)
require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle)
t.Log("By checking the expected installed conditions")
@@ -674,7 +669,6 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) {
require.NoError(t, cl.Get(ctx, extKey, clusterExtension))
t.Log("By checking the status fields")
- require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Resolution.Bundle)
require.Equal(t, ocv1alpha1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, clusterExtension.Status.Install.Bundle)
t.Log("By checking the expected installed conditions")
diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go
index 1ef5a18cf..8210f3999 100644
--- a/internal/controllers/common_controller.go
+++ b/internal/controllers/common_controller.go
@@ -48,10 +48,6 @@ func setInstalledStatusConditionFailed(ext *ocv1alpha1.ClusterExtension, message
})
}
-func setResolutionStatus(ext *ocv1alpha1.ClusterExtension, resStatus *ocv1alpha1.ClusterExtensionResolutionStatus) {
- ext.Status.Resolution = resStatus
-}
-
func setInstallStatus(ext *ocv1alpha1.ClusterExtension, installStatus *ocv1alpha1.ClusterExtensionInstallStatus) {
ext.Status.Install = installStatus
}
diff --git a/test/e2e/cluster_extension_install_test.go b/test/e2e/cluster_extension_install_test.go
index 351f777f5..d2d871036 100644
--- a/test/e2e/cluster_extension_install_test.go
+++ b/test/e2e/cluster_extension_install_test.go
@@ -268,13 +268,6 @@ func TestClusterExtensionInstallRegistry(t *testing.T) {
t.Log("By eventually reporting a successful resolution and bundle path")
require.EventuallyWithT(t, func(ct *assert.CollectT) {
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: fmt.Sprintf("%s-operator.1.2.0", tc.packageName),
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
t.Log("By eventually reporting no longer progressing")
@@ -332,7 +325,6 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) {
t.Log("By eventually reporting a failed resolution with multiple bundles")
require.EventuallyWithT(t, func(ct *assert.CollectT) {
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
- assert.Nil(ct, clusterExtension.Status.Resolution)
}, pollDuration, pollInterval)
t.Log("By eventually reporting Progressing == True and Reason Retrying")
@@ -377,13 +369,6 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) {
t.Log("By eventually reporting a successful installation")
require.EventuallyWithT(t, func(ct *assert.CollectT) {
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.0.0",
- Version: "1.0.0",
- }},
- clusterExtension.Status.Resolution,
- )
assert.Equal(ct,
&ocv1alpha1.ClusterExtensionInstallStatus{Bundle: ocv1alpha1.BundleMetadata{
Name: "prometheus-operator.1.0.0",
@@ -392,7 +377,7 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) {
clusterExtension.Status.Install,
)
- cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing)
+ cond = apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1alpha1.TypeProgressing)
if !assert.NotNil(ct, cond) {
return
}
@@ -408,7 +393,6 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) {
t.Log("By eventually reporting an unsatisfiable resolution")
require.EventuallyWithT(t, func(ct *assert.CollectT) {
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
- assert.Empty(ct, clusterExtension.Status.Resolution)
}, pollDuration, pollInterval)
t.Log("By eventually reporting Progressing == True and Reason Retrying")
@@ -457,14 +441,6 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.0.0",
- Version: "1.0.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
t.Log("It allows to upgrade the ClusterExtension to a non-successor version")
@@ -482,14 +458,6 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.2.0",
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
}
@@ -526,14 +494,6 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.0.0",
- Version: "1.0.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
t.Log("It does allow to upgrade the ClusterExtension to any of the successor versions within non-zero major version")
@@ -550,14 +510,6 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.0.1",
- Version: "1.0.1",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
}
@@ -604,14 +556,6 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.2.0",
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
// patch imageRef tag on test-catalog image with v2 image
@@ -638,14 +582,6 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.2.0.0",
- Version: "2.0.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
}
@@ -704,14 +640,6 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.2.0",
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
// update tag on test-catalog image with v2 image
@@ -738,14 +666,6 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) {
}
assert.Equal(ct, metav1.ConditionFalse, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
-
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.2.0.0",
- Version: "2.0.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
}
@@ -787,13 +707,6 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T
assert.Equal(ct, metav1.ConditionTrue, cond.Status)
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
assert.Contains(ct, cond.Message, "Installed bundle")
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.2.0",
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
t.Log("By deleting a managed resource")
@@ -853,13 +766,6 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
t.Log("By eventually reporting a successful resolution and bundle path")
require.EventuallyWithT(t, func(ct *assert.CollectT) {
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
- assert.Equal(ct,
- &ocv1alpha1.ClusterExtensionResolutionStatus{Bundle: ocv1alpha1.BundleMetadata{
- Name: "prometheus-operator.1.2.0",
- Version: "1.2.0",
- }},
- clusterExtension.Status.Resolution,
- )
}, pollDuration, pollInterval)
t.Log("By eventually reporting Progressing == True with Reason Retrying")
diff --git a/test/upgrade-e2e/post_upgrade_test.go b/test/upgrade-e2e/post_upgrade_test.go
index 7159db733..8bb2ae3df 100644
--- a/test/upgrade-e2e/post_upgrade_test.go
+++ b/test/upgrade-e2e/post_upgrade_test.go
@@ -107,7 +107,6 @@ func TestClusterExtensionAfterOLMUpgrade(t *testing.T) {
}
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
assert.Contains(ct, cond.Message, "Installed bundle")
- assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Resolution.Bundle)
assert.Equal(ct, ocv1alpha1.BundleMetadata{Name: "prometheus-operator.1.0.1", Version: "1.0.1"}, clusterExtension.Status.Install.Bundle)
assert.NotEqual(ct, previousVersion, clusterExtension.Status.Install.Bundle.Version)
}, time.Minute, time.Second)