Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix E2E panic due to invalid nil check #1447

Merged
merged 2 commits into from
Oct 31, 2023

Conversation

tpantelis
Copy link
Contributor

This code results in a panic if the underlying value of result is nil:

func(result interface{}) (bool, string, error) {
    if result == nil {
        return false, "gateway not found yet", nil
    }

    gw := result.(*unstructured.Unstructured)
    haStatus := NestedString(gw.Object, "status", "haStatus") => PANIC
    ...
})

But it first checks if result is nil so how can this be? A simple nil check does not work because interfaces in Go contain both a type and a value. In this case, we want to check for nil after casting.

This code results in a panic if the underlying value of 'result'
is nil:

func(result interface{}) (bool, string, error) {
    if result == nil {
        return false, "gateway not found yet", nil
    }

    gw := result.(*unstructured.Unstructured)
    haStatus := NestedString(gw.Object, "status", "haStatus") => PANIC
    ...
})

But it first checks if 'result' is nil so how can this be? A simple nil check
does not work because interfaces in Go contain both a type and a value. In
this case, we want to check for nil after casting.

Signed-off-by: Tom Pantelis <[email protected]>
@tpantelis tpantelis self-assigned this Oct 30, 2023
@submariner-bot
Copy link

🤖 Created branch: z_pr1447/tpantelis/fix_nil_check

@tpantelis tpantelis changed the title Fix panic due to invalid nil check Fix E2Ecpanic due to invalid nil check Oct 31, 2023
@tpantelis tpantelis changed the title Fix E2Ecpanic due to invalid nil check Fix E2E panic due to invalid nil check Oct 31, 2023
@Jaanki Jaanki linked an issue Oct 31, 2023 that may be closed by this pull request
@sridhargaddam sridhargaddam enabled auto-merge (rebase) October 31, 2023 08:04
@sridhargaddam sridhargaddam merged commit b45d8d8 into submariner-io:devel Oct 31, 2023
43 checks passed
@submariner-bot
Copy link

🤖 Closed branches: [z_pr1447/tpantelis/fix_nil_check]

tpantelis added a commit to tpantelis/shipyard that referenced this pull request Nov 6, 2023
PR submariner-io#1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to casting first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil, eg
      var s string
      var i interface{} = s
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for submariner-io#1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result fucntion yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo submariner-io#1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit to tpantelis/shipyard that referenced this pull request Nov 6, 2023
PR submariner-io#1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to cast first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for submariner-io#1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result function yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo submariner-io#1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit to tpantelis/shipyard that referenced this pull request Nov 6, 2023
PR submariner-io#1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to cast first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for submariner-io#1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result function yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo submariner-io#1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit that referenced this pull request Nov 6, 2023
PR #1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to cast first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for #1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result function yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo #1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit to tpantelis/shipyard that referenced this pull request Nov 6, 2023
PR submariner-io#1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to cast first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for submariner-io#1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result function yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo submariner-io#1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
tpantelis added a commit that referenced this pull request Nov 6, 2023
PR #1447 changed places
where we check for nil interface{} in the callback functions for
AwaitUntil to cast first. However this can result in an invalid type
conversion panic depending on how the value is returned from the
operation function. In Go, an interface var contains both a type and
value so there are 2 cases for a nil:

 1) The type is non-nil and the value is nil
 2) The type and value are nil

For #1, the interface{} var is not actually nil so checking
'if result == nil' yields false. Eg,

  var s *string
  var i interface{} = s

  if i == nil { // yields false b/c i is typed
  }

In this case, we can cast to the expected type and then check for nil.

  s := i.(*string)
  if s == nil {  // yields true
  }

For #2, the interface{} var is untyped so nil check yields true:

  var i interface{} = nil
  if i == nil { // yields true
  }

So in cases where we directly return nil from an operation function, the
nil check in the result function is valid.

The actual reason for #1447 was b/c of a nil pointer panic in
AwaitGatewaysWithStatus. This function was previously changed to call
'findGateway' and return its values. However 'findGateway' returns
*unstructured.Unstructured so now the operation function returned
a typed interface{} var. So if 'findGateway' returns a nil
*unstructured.Unstructured value, the 'result == nil' check in the
result function yields false. This resulted in the panic when 'result'
was later casted and accessed.

So we basically need to undo #1447. For AwaitGatewayWithStatus and
AwaitGatewayFullyConnected, check the error from 'findGateway' and
return a nil interface{} value.

Signed-off-by: Tom Pantelis <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Improvements to the Gateway leader election
4 participants